Android-源码解析HandlerLooper
Handler是可以用來發送消息,或者一個Runable;消息/Runable可以在指定的線程(由Handler中的Looper決定)中接受/執行; 
 MessageQueue:消息隊列,用來存放消息的 
 Looper:用來循環遍歷消息隊列中的消息,并負責消息的分發
我們看看使用Handler常用示例代碼(如下
public class TestActivity extends Activity {private Handler mHandler = new Handler(){@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case caseA:doSomeThingA();break;case caseB;doSomeThingB();break;default:break;}}};@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);sendMessage();}private void sendMessage() {Message msg = Message.obtain();msg.what = caseA;msg.obj = data;mHandler.sendMessage(msg);} }我們可以看到mHandler變量作為成員變量,覆寫了handleMessage方法,且是通過無參構造器創建出來的; 
 在onCreate方法調用了sendMessage方法,這樣消息最終被傳送到handlerMessage中;下面我們結合Handler部分源碼看看消息是如何一步一步到達Handler的handleMessage方法里的。
TestActivity類中的sendMessage方法中發送消息后,調用了mHandler.sendMessage(msg),也就是如下代碼,
public final boolean sendMessage(Message msg) {return sendMessageDelayed(msg, 0); }public final boolean sendMessageDelayed(Message msg, long delayMillis) {if (delayMillis < 0) {delayMillis = 0;}return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); }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); }private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {msg.target = this;if (mAsynchronous) {msg.setAsynchronous(true);}return queue.enqueueMessage(msg, uptimeMillis); }我們從上述源碼可以看到,handler發送的消息最終是把消息放到消息隊列中,之后就沒做任何事情了,那么問題來了
那么消息隊列是在什么時候創建?
其實每個Looper都包含一個消息隊列成員,而消息隊列也是在Looper構造器時被創建;
private Looper(boolean quitAllowed) {mQueue = new MessageQueue(quitAllowed);mThread = Thread.currentThread(); }我們看到Handler類中包含了Looper 和 消息隊列成員變量,它們都是在構造器中被賦值(見下面Handler類部分源碼)
/*** Default constructor associates this handler with the {@link Looper} for the* current thread.** If this thread does not have a looper, this handler won't be able to receive messages* so an exception is thrown.*/public Handler() {this(null, false);} /*** Use the {@link Looper} for the current thread with the specified callback interface* and set whether the handler should be asynchronous.** Handlers are synchronous by default unless this constructor is used to make* one that is strictly asynchronous.** Asynchronous messages represent interrupts or events that do not require global ordering* with respect to synchronous messages. Asynchronous messages are not subject to* the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.** @param callback The callback interface in which to handle messages, or null.* @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for* each {@link Message} that is sent to it or {@link Runnable} that is posted to it.** @hide*/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());}}//獲取當前線程的Looper對象mLooper = Looper.myLooper();if (mLooper == null) {throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");}//從Looper對象中獲取消息隊列mQueue = mLooper.mQueue;mCallback = callback;mAsynchronous = async;}因為示例代碼中Handler對象是在UI線程中創建,而系統在初始化時會自動調用Looper.prepareMainLooper方法,該方法幫我們創建好了主線程的Looper對象(所以在UI線程中我們不需要主動調用Looper.prepareMainLooper去創建Looper對象);創建Looper對象后會啟用loop方法,它會不斷循環讀取消息隊列的消息。
我們看下Looper源碼:
package android.os; import android.annotation.NonNull; import android.annotation.Nullable; import android.util.Log; import android.util.Printer;/*** Class used to run a message loop for a thread. Threads by default do* not have a message loop associated with them; to create one, call* {@link #prepare} in the thread that is to run the loop, and then* {@link #loop} to have it process messages until the loop is stopped.** <p>Most interaction with a message loop is through the* {@link Handler} class.** <p>This is a typical example of the implementation of a Looper thread,* using the separation of {@link #prepare} and {@link #loop} to create an* initial Handler to communicate with the Looper.** <pre>* class LooperThread extends Thread {* public Handler mHandler;** public void run() {* Looper.prepare();** mHandler = new Handler() {* public void handleMessage(Message msg) {* // process incoming messages here* }* };** Looper.loop();* }* }</pre>*/ public final class Looper {/** API Implementation Note:** This class contains the code required to set up and manage an event loop* based on MessageQueue. APIs that affect the state of the queue should be* defined on MessageQueue or Handler rather than on Looper itself. For example,* idle handlers and sync barriers are defined on the queue whereas preparing the* thread, looping, and quitting are defined on the looper.*/private static final String TAG = "Looper";// sThreadLocal.get() will return null unless you've called prepare().static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();private static Looper sMainLooper; // guarded by Looper.classfinal MessageQueue mQueue;final Thread mThread;private Printer mLogging;/** Initialize the current thread as a looper.* This gives you a chance to create handlers that then reference* this looper, before actually starting the loop. Be sure to call* {@link #loop()} after calling this method, and end it by calling* {@link #quit()}.*/public static void prepare() {prepare(true);}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));}/*** Initialize the current thread as a looper, marking it as an* application's main looper. The main looper for your application* is created by the Android environment, so you should never need* to call this function yourself. See also: {@link #prepare()}*/public static void prepareMainLooper() {prepare(false);synchronized (Looper.class) {if (sMainLooper != null) {throw new IllegalStateException("The main Looper has already been prepared.");}sMainLooper = myLooper();}}/*** Returns the application's main looper, which lives in the main thread of the application.*/public static Looper getMainLooper() {synchronized (Looper.class) {return sMainLooper;}}/*** 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 blockif (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 loggerPrinter logging = me.mLogging;if (logging != null) {logging.println(">>>>> Dispatching to " + msg.target + " " +msg.callback + ": " + msg.what);}//target其實就是Handler,此處就是將消息傳遞到Handler中dispatchMessage方法中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.recycleUnchecked();}}/*** Return the Looper object associated with the current thread. Returns* null if the calling thread is not associated with a Looper.*/public static @Nullable Looper myLooper() {return sThreadLocal.get();}/*** Return the {@link MessageQueue} object associated with the current* thread. This must be called from a thread running a Looper, or a* NullPointerException will be thrown.*/public static @NonNull MessageQueue myQueue() {return myLooper().mQueue;}private Looper(boolean quitAllowed) {mQueue = new MessageQueue(quitAllowed);mThread = Thread.currentThread();} }我們可以看到上述代碼中loop方法,其內部就是不斷從消息隊列中取消息,最終調用msg.target.dispatchMessage(msg);這里的target其實就是handler對象,也就是Handler的dispatchMessage方法;那么dispatchMessage內部是怎么實現?(看下面源碼)
/*** Handle system messages here.*/public void dispatchMessage(Message msg) {if (msg.callback != null) {//如果Message中的Runnable不空,直接調用Message的Runable.run方法handleCallback(msg);} else {//如果Handler中構造器傳入了CallBack不為空if (mCallback != null) {//如果CallBack不為空且handleMessage返回為true,直接返回(不執行handleMessage方法了)if (mCallback.handleMessage(msg)) {return;}}handleMessage(msg);}}private static void handleCallback(Message message) {message.callback.run();}public interface Callback {public boolean handleMessage(Message msg);}我們的示例代碼中發送的message的callback沒有賦值所以為null,且我們的mHandler是無參構造的所以mCallback其實也是為空,所以最終執行到Handler的handleMessage方法;整個大體流程就是這樣;
我們可以看到其實dispatchMessage方法運行在哪個線程其實是由Looper.loop決定的,至此整個流程逐漸清晰明朗起來了
梳理下流程:
Created with Rapha?l 2.1.0HandlerHandlerMessageQueueMessageQueueLooperLooper(1)sendMessage (調用在A線程)最終是將消息添加到消息隊列中(2) Looper不斷從消息隊列中取消息(3)dispatchMessage(運行在loop調用的線程)(4)處理消息通過Looper&Handler來達到線程間通訊
上面示例代碼演示消息最終在UI線程被消費處理掉,如果要將一個消息/Runable傳遞給一個線程我們該如何做呢?我么可以這樣做
class LooperThread extends Thread {private Handler mHandler;@Overridepublic void run() {super.run();//為當前線程創建一個Looper對象Looper.prepare();//handler會獲取當前線程綁定的Looper對象mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);}};//循環分發消息隊列中的消息Looper.loop();}}首先定義一個LooperThread,我們覆寫run方法,并對該線程綁定一個Looper對象和一個Handler對象,這樣在外部我們可以通過Handler對象將消息/Runable交給Handler的handlerMessage處理了;這樣就達到了其他線程消息交給此線程處理目的,這其實就實現了線程間通訊了。
總結
以上是生活随笔為你收集整理的Android-源码解析HandlerLooper的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: Android - aar文件资源重复引
- 下一篇: Java多线程-CountDownLat
