Communication principle between Android threads
To understand the principle, read the fucking source
1. Start with HandlerThread.
HandlerThread is a class provided by the android system. It inherits the Thread and is a Thread. See the run method.
public void run() { mTid = Process.myTid(); Looper.prepare();// #1 synchronized (this) { mLooper = Looper.myLooper();// #2 notifyAll(); } Process.setThreadPriority(mPriority); onLooperPrepared(); Looper.loop();// #3 mTid = -1; }2. Relationship between Thread and logoff
Among the 1st labels in the code segment, the prepare method of logoff is called.
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));// #4 }
In the code segment's 4th identifiers, store the logoff instance in the thread's local variable ThreadLocal and bind the logoff with the current thread.
In the code segment's 2nd identifiers, the Looper instance bound to the current thread is obtained, and the current thread has a Looper instance.
In the code segment's 3rd identifiers, the logoff loop () method and the loop () method code are called:
/** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the 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; // 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 Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } msg.target.dispatchMessage(msg); 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.recycle(); } }
The loop () method is used to obtain the MessageQueue queue from logoff, retrieve the Message from MessageQueue, and send the Message through Handler until the result is obtained. Someone may ask where to wake up the thread after the Message is obtained and continue to obtain the Message cyclically? Yes, it is to wake up when a message is inserted into MessageQueue after Handler's sendMessage is called, and call the local method nativeWake (mPtr ).
3. Relationship between Handler and logoff
Most commonly, we create a Handler object in the Activity to update the UI.
private Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { // TODO Auto-generated method stub super.handleMessage(msg); } };
Let's look at the Handler constructor.
public Handler() { this(null, false); } public Handler(Callback callback, boolean async) { if (FIND_POTENTIAL_LEAKS) { final Class
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 that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; }
As you can see, in the constructor, Handler already has the logoff instance of the current thread and the MessageQueue queue of The logoff object.
Speaking of this, we should also understand the relationship between them and how messages are exchanged between threads. The Handler sends messages by inserting messages into the MessageQueue queue.
public boolean sendMessageAtTime(Message msg, long uptimeMillis) { MessageQueue queue = mQueue; if (queue == null) { RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); return false; } return enqueueMessage(queue, msg, uptimeMillis); }
When the Handler's enqueueMessage method is called, The MessageQueue's enqueueMessage
final boolean enqueueMessage(Message msg, long when) { if (msg.isInUse()) { throw new AndroidRuntimeException(msg + " This message is already in use."); } if (msg.target == null) { throw new AndroidRuntimeException("Message must have a target."); } boolean needWake; synchronized (this) { if (mQuiting) { RuntimeException e = new RuntimeException( msg.target + " sending message to a Handler on a dead thread"); Log.w("MessageQueue", e.getMessage(), e); return false; } msg.when = when; Message p = mMessages; 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; } } if (needWake) { nativeWake(mPtr);// #5 } return true; }
In code snippet 5, the nativeWake (mPtr) method is called, which is a local method used to wake up the Thread.
References:
Message Queue mechanism of android