簡述:
Handler在Android裏面到處可見。一般用於多線程消息交互、主線程刷新、延時/定時處理等。今天來揭開Handler背後的祕密。
PS: 如果有想替代Handler用開源庫的話,大名鼎鼎的RxJava就是幹這事的。
相關代碼:
- frameworks/base/core/java/android/os/Handler.java
- frameworks/base/core/java/android/os/MessageQueue.java
- frameworks/base/core/java/android/os/Looper.java
介紹:
從Handler默認構造函數入手:
110 /**
111 * Default constructor associates this handler with the {@link Looper} for the
112 * current thread.
113 *
114 * If this thread does not have a looper, this handler won't be able to receive messages
115 * so an exception is thrown.
116 */
117 public Handler() {
118 this(null, false);
119 }
從註釋可以看出,Handler綁定了Looper,而Looper又記錄了當前線程等信息,且認爲是Handler跟線程強相關。
找到參數爲(null, false)的方法,
175 /**
176 * Use the {@link Looper} for the current thread with the specified callback interface
177 * and set whether the handler should be asynchronous.
178 *
179 * Handlers are synchronous by default unless this constructor is used to make
180 * one that is strictly asynchronous.
181 *
182 * Asynchronous messages represent interrupts or events that do not require global ordering
183 * with respect to synchronous messages. Asynchronous messages are not subject to
184 * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
185 *
186 * @param callback The callback interface in which to handle messages, or null.
187 * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
188 * each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
189 *
190 * @hide
191 */
192 public Handler(Callback callback, boolean async) {
193 if (FIND_POTENTIAL_LEAKS) {
194 final Class<? extends Handler> klass = getClass();
195 if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
196 (klass.getModifiers() & Modifier.STATIC) == 0) {
197 Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
198 klass.getCanonicalName());
199 }
200 }
201
202 mLooper = Looper.myLooper();
203 if (mLooper == null) {
204 throw new RuntimeException(
205 "Can't create handler inside thread " + Thread.currentThread()
206 + " that has not called Looper.prepare()");
207 }
208 mQueue = mLooper.mQueue;
209 mCallback = callback;
210 mAsynchronous = async;
211 }
關鍵代碼mLooper = Looper.myLooper()繼續進去看
70 // sThreadLocal.get() will return null unless you've called prepare().
71 static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
...
250 /**
251 * Return the Looper object associated with the current thread. Returns
252 * null if the calling thread is not associated with a Looper.
253 */
254 public static @Nullable Looper myLooper() {
255 return sThreadLocal.get();
256 }
從sThreadlocal裏面取出Looper。sThreadLocal,看名字意思就可以大概知道線程本地記錄,他可以記錄了一個Looper對象(要詳細瞭解ThreadLocal,可以搜下)。既然是get,一般對應的可以找到set的地方。找遍Looper代碼可以發現只有一個set的地方:
91 /** Initialize the current thread as a looper.
92 * This gives you a chance to create handlers that then reference
93 * this looper, before actually starting the loop. Be sure to call
94 * {@link #loop()} after calling this method, and end it by calling
95 * {@link #quit()}.
96 */
97 public static void prepare() {
98 prepare(true);
99 }
100
101 private static void prepare(boolean quitAllowed) {
102 if (sThreadLocal.get() != null) {
103 throw new RuntimeException("Only one Looper may be created per thread");
104 }
105 sThreadLocal.set(new Looper(quitAllowed));
106 }
從代碼可以看出,每個線程只會有一個Looper。prepare只能調用一次,如果調用第二次,就會丟出RuntimeException。繼續看Looper的構造函數:
267 private Looper(boolean quitAllowed) {
268 mQueue = new MessageQueue(quitAllowed);
269 mThread = Thread.currentThread();
270 }
Looper創建了一個MessageQueue,同時引用了當前線程對象。到這裏,Handler多線程消息交互,就可以推測出,應該就是靠的這個mQueue了(參數quitAllowed用於區別當前線程是否是主線程)。
我們回到Handler的構造函數往下看,203行會有一個判斷mLooper是否爲空。如果是空,就會丟出異常。這裏是爲了保證在Handler對象初始化的時候,這個mLooper一定綁定過一個線程,也就是一定調用過Looper的prepare方法。在Looper裏面的類介紹裏面也給出了示例用法:
27 /**
28 * Class used to run a message loop for a thread. Threads by default do
29 * not have a message loop associated with them; to create one, call
30 * {@link #prepare} in the thread that is to run the loop, and then
31 * {@link #loop} to have it process messages until the loop is stopped.
32 *
33 * <p>Most interaction with a message loop is through the
34 * {@link Handler} class.
35 *
36 * <p>This is a typical example of the implementation of a Looper thread,
37 * using the separation of {@link #prepare} and {@link #loop} to create an
38 * initial Handler to communicate with the Looper.
39 *
40 * <pre>
41 * class LooperThread extends Thread {
42 * public Handler mHandler;
43 *
44 * public void run() {
45 * Looper.prepare();
46 *
47 * mHandler = new Handler() {
48 * public void handleMessage(Message msg) {
49 * // process incoming messages here
50 * }
51 * };
52 *
53 * Looper.loop();
54 * }
55 * }</pre>
56 */
繼續構造函數的第208行,這裏有個關鍵變量。mQueue的賦值:
208 mQueue = mLooper.mQueue;
到這裏,可以總結出:
同一個線程裏面,可以有多個Handler,最多隻有一個Looper對象,多個Handler用的是同一個MessageQueue。
寫段測試代碼證實下:
package com.fy.platfromdebug;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.MessageQueue;
import android.util.Log;
public class TestHandler {
private static final String TAG = "TestHandler";
public void test() {
Handler mainHandler = new Handler(Looper.getMainLooper());
MessageQueue queue = mainHandler.getLooper().getQueue();
Log.d(TAG, "mainHandler=" + mainHandler + " looper=" + mainHandler.getLooper() + " queue=" + queue);
Handler mainHandler2 = new Handler(Looper.getMainLooper());
MessageQueue queue2 = mainHandler2.getLooper().getQueue();
Log.d(TAG, "mainHandler2=" + mainHandler2 + " looper=" + mainHandler2.getLooper() + " queue=" + queue2);
}
public void test2() {
HandlerThread handlerThread = new HandlerThread("Test");
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper());
Log.d(TAG, "handler=" + handler + " looper=" + handler.getLooper() + " queue=" + handler.getLooper().getQueue());
Handler handler2 = new Handler(handlerThread.getLooper());
Log.d(TAG, "handler2=" + handler2 + " looper=" + handler2.getLooper() + " queue=" + handler2.getLooper().getQueue());
}
}
從log輸出,可以證實我們的分析。
2019-01-07 10:26:30.492 22032-22032/com.fy.platfromdebug D/TestHandler: mainHandler=Handler (android.os.Handler) {459885c} looper=Looper (main, tid 2) {4d34765} queue=android.os.MessageQueue@ad45c3a
2019-01-07 10:26:30.493 22032-22032/com.fy.platfromdebug D/TestHandler: mainHandler2=Handler (android.os.Handler) {f6120eb} looper=Looper (main, tid 2) {4d34765} queue=android.os.MessageQueue@ad45c3a
2019-01-07 10:26:30.496 22032-22032/com.fy.platfromdebug D/TestHandler: handler=Handler (android.os.Handler) {524ce48} looper=Looper (Test, tid 1812) {b35f4e1} queue=android.os.MessageQueue@d9c0206
2019-01-07 10:26:30.496 22032-22032/com.fy.platfromdebug D/TestHandler: handler2=Handler (android.os.Handler) {aaee0c7} looper=Looper (Test, tid 1812) {b35f4e1} queue=android.os.MessageQueue@d9c0206
這裏有個疑問,平時我們實例化Handler時,也沒有傳過任何參數,那Handler是綁定的哪個線程呢?我們也沒有調用過Looper.prepare(),代碼也沒丟過異常。
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
...
}
};
綁定的線程很好理解,就是實例化代碼運行在哪個線程,就綁定的哪個線程。
Looper.prepare()雖然我們沒調用過,不代表別人沒調用過。上面的代碼一般是運行在主線程,那我們肯定可以找到調用parpare的地方。在Looper裏面搜索調用prepare的地方,發現Looper裏面還有一個方法:
108 /**
109 * Initialize the current thread as a looper, marking it as an
110 * application's main looper. The main looper for your application
111 * is created by the Android environment, so you should never need
112 * to call this function yourself. See also: {@link #prepare()}
113 */
114 public static void prepareMainLooper() {
115 prepare(false);
116 synchronized (Looper.class) {
117 if (sMainLooper != null) {
118 throw new IllegalStateException("The main Looper has already been prepared.");
119 }
120 sMainLooper = myLooper();
121 }
122 }
這裏調用了prepare方法,而這個prepareMainLooper在ActivityThread的main方法裏面就調用了。所以前面的Handler綁定的是主線程。
6804 public static void main(String[] args) {
...
6822
6823 Looper.prepareMainLooper();
6824
...
6853 }
到現在爲止,我們已經搞清楚Handler跟線程之前的關係了。
下次我們再來揭祕Handler背後的另一個boss, MessageQueue。