Handler message mechanism and handler Mechanism
Handler message mechanism
Handler is mainly used for communication between two threads.
It mainly consists of logoff, messageQueue, message, and handler.
When the App is started, the ActivityThread class is first executed,
Public static void More... main (String [] args) {SamplingProfilerIntegration. start (); // CloseGuard defaults to true and can be quite spammy. we // disable it here, but selectively enable it later (via // StrictMode) on debug builds, but using DropBox, not logs. closeGuard. setEnabled (false); Environment. initForCurrentUser (); // Set the reporter for event logging in libcore EventLogger. setReporter (new EventLoggingReporter (); Security. addProvider (new AndroidKeyStoreProvider (); Process. setArgV0 (""); Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } AsyncTask.init(); if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }There are too many things in the class. You only need to check logoff. prepareMainLooper (); you can know that when the App is started, a Looper is created for us. Because of the ActivityThread main thread, Looper is also in the main thread.
When the App is started, logoff calls the loop () method. Let's take a look at the specific implementation of loop. There are many codes, but the main meaning is to retrieve the message from the MessageQueue queue, and then distribute the message by handler.
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; // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); 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); } final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs; final long traceTag = me.mTraceTag; if (traceTag != 0 && Trace.isTagEnabled(traceTag)) { Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); } final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis(); final long end; try { msg.target.dispatchMessage(msg); end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis(); } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } if (slowDispatchThresholdMs > 0) { final long time = end - start; if (time > slowDispatchThresholdMs) { Slog.w(TAG, "Dispatch took " + time + "ms on " + Thread.currentThread().getName() + ", h=" + msg.target + " cb=" + msg.callback + " msg=" + msg.what); } } if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); } msg.recycleUnchecked(); } }The following describes the preparemainlogoff (), prepare (), and logoff () Methods of The logoff class.
public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); }}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)); } public static @Nullable Looper myLooper() { return sThreadLocal.get(); } private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }It can be seen from the above that a logoff corresponds to a MessageQueue.
Next, let's take a look at the MessageQueue class. The messageQueue enters the queue code. The logoff. loop () method called above is out of the queue.
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 { // Inserted within the middle of the queue. Usually we don't have to wake // up the event queue unless there is a barrier at the head of the queue // and the message is the earliest asynchronous message in the queue. 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; }The Message is sorted by time when it enters the queue.
Finally, let's take a look at the Message class. Message is essentially a Data Structure of a one-way linked list, with nothing special.
To sum up, when the App is started, a logoff object is created first. However, a MessageQueue object is created in the logoff object structure. When the prepare () method is called, The logoff object is created and saved to the ThreadLocal object. When the logoff object is created again, an exception is thrown.
"Only one Looper may be created per thread", so we can think that a thread can Only have one Looper object and one MessageQueue object. When handler sends a message, messages are sorted by time priority in the MessageQueue queue. When the App is started, logoff will call the loop method to retrieve messages from MessageQueue, call dispatchMessage, send the messages back to handler, and then handler will process the messages according to the actual situation.