Analysis of the source code from the point of view Handler message mechanism

In fact, the Internet has a lot of very good well written article, I wanted to write this is to stroke clear thinking, and enhance memory.

Here recommend an excellent article:

https://blog.csdn.net/zip_tts/article/details/86097136#commentBox

Message Handler mechanism mainly has five components, Handler, Looper, Message, MessageQueue, ThreadLocal

  • Message Handler As the processing center for processing messages and sending messages.
  • Looper as a power message for acquiring the distribution message.
  • Message message as a carrier for carrying the message.
  • MessageQueue as a message queue for storing messages.
  • ThreadLocal is an internal thread class data storage, data can be obtained only in the specified thread.
    **

First, what is the Message

Above all, to understand what we want Message transmission is what is inside.

public final class Message implements Parcelable {
	public int what;
    public int arg1;
    public int arg2;
    public Object obj;
    public Messenger replyTo;
    int flags;
    long when;
    Bundle data;
    Handler target;
    }

You are not feel quite familiar? We all need to write a lot of methods used.
Here are a target, Handler is the type of that which is to store the Message Handler's Message, which help us to understand later.

Two, Handler and send instantiation

Examples of handler seven constructor

public Handler() {
    this(null, false);
}

public Handler(Callback callback) {
    this(callback, false);
}

public Handler(Looper looper) {
    this(looper, null, false);
}

public Handler(Looper looper, Callback callback) {
    this(looper, callback, false);
}
   
public Handler(boolean async) {
    this(null, async);
}

public Handler(Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) {
        final Class<? extends Handler> klass = getClass();
        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                (klass.getModifiers() & Modifier.STATIC) == 0) {
            Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                klass.getCanonicalName());
        }
    }
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread " + Thread.currentThread()
                    + " that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

public Handler(Looper looper, Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

We can see from the code, where the thread Looper Looper if not specified, is used to instantiate Handler.
About sending Handler, in fact, there are many methods, but the look can be found, the final methods are directed enqueueMessage, and are encapsulates the message Message, and then sent out.

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

The above code, the Message of the target point to themselves, transmits to the MessageQueue, well understood, the following analyzes MessageQueue enququqMessage ()

三、MessageQueue

boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException(“Message must have a target.”);
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}

    synchronized (this) {
        if (mQuitting) {
            IllegalStateException e = new IllegalStateException(
                    msg.target + " sending message to a Handler on a dead thread");
            Log.w(TAG, e.getMessage(), e);
            msg.recycle();
            return false;
        }

        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        if (p == null || when == 0 || when < p.when) {
            // New head, wake up the event queue if blocked.
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } else {
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            for (;;) {
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            msg.next = p; // invariant: p == prev.next
            prev.next = msg;
        }

        // We can assume mPtr != 0 because mQuitting is false.
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

In fact, the message queue, the message on the time stored in the linked list, if the delay is not set sequentially, there would sorted by time.

Four, Looper

The following is Looper constructor:

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

You can see that it's constructor is private, ie Looper is not by new method to create an object, then it will certainly be other ways you can get. Continue to view the source code found inside it there prepareMainLooper, prepare method,

public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

Looper prepareMainLooper mainly used to create the main thread, under normal circumstances, is prohibited, because there is always the main thread Looper. And you can see, the main way to create or prepare ().

 private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

That is where the ThreadLocal

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

Obviously, this creates the thread Looper out, and to keep in a ThreadLocal.
So now Looper has been created out how to use it, send it to the Message MessageQueue? That's Loop ()

 public static void loop() {
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;
    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

        // This must be in a local variable, in case a UI event sets the logger
        final Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }


        try {
            msg.target.dispatchMessage(msg);
            end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
    }

I deleted some code, almost above the core code. MessageQueue essentially on a single list, Loop is used for (;;), is an infinite loop, stop the Message MessageQueue get through next, if it is empty directly return, or by msg.target.dispatchMessage(msg)sending the information to the handler msg.target type of dispatchMessage go.

Five, handler acquisition and processing

Then the code came to the handler in dispatchMessage () in

 public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

Here are some judgment statement. In Handler, declare an interface Callback, there is only one way handleMessage, this approach is to deal with the Message of, Callback passed in the constructor. Of course, if no incoming Callback in the constructor if there is a way, Handler also defines a handleMessage method, the default is empty implementation. It may be processed by the Message override this method.
So this whole process came to an end.

Published 57 original articles · won praise 3 · Views 6220

Guess you like

Origin blog.csdn.net/qq_39830579/article/details/88738717