Handler使用場景以及原始碼分析
路漫漫其修遠兮,吾將上下而求索
Handler的使用場景
子執行緒操作完成之後,通知主執行緒執行操作
首先在主執行緒建立一個Handler例項
private val MSG_WHAT: Int = 1000 private var mHandler = object : Handler() { override fun handleMessage(msg: Message?) { when (msg?.what) { MSG_WHAT -> { val isMainThread = msg.obj as
然後在子執行緒操作(這裡的操作是判斷該執行緒是否是主執行緒)完成之後,通知主執行緒:
private fun checkIsMainThread() {
thread {
val message = Message()
message.what = MSG_WHAT
message.obj = isMainThread()
mHandler.sendMessage(message)
}.start()
}
- 判斷執行程式碼的執行緒是否是主執行緒
有時候我們需要判斷當前執行緒是否是主執行緒,這個時候就可以用帶Looper了,通過Looper,我們就可以知道當前執行緒是否為主執行緒:
private fun isMainThread(): Boolean {
return Looper.myLooper() == Looper.getMainLooper()
}
在子執行緒中直接切換到主執行緒執行程式碼
開發中經常遇到的一個場景就是在子執行緒中執行一段操作之後(比如網路請求,IO),然後在主執行緒中更新UI,但是又沒有Context,也不想在新建一個Handler的Field接收訊息,這個時候就可以利用Handler的構造方法了,在構造方法中傳入一個Looper引數,這裡獲取的是主執行緒的Looper例項:
private fun directUpdateUI() {
thread {
Thread.sleep(1000)
Handler(Looper.getMainLooper()).post {
btnDirectUpdateUI.text = "UI已經更新了"
}
}.start()
}
當然,這個方法也可以用在兩個子執行緒之間的通訊上。
runOnUiThread(Runnable action)方法
在Activity中,系統為我們提供了一個一個runOnUiThread方法,可以方便的在主執行緒中執行操作,而這個方法本質上也是通過Handler進行的操作:
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
這裡的mHandler是在主執行緒中建立的,如果當前執行緒不是主執行緒,則利用主執行緒的Handler進行post操作,如果是主執行緒,則直接執行。
原始碼解析
Looper.prepare()
在建立Handler例項之前,必須要先有Looper例項,至於我們平時在主執行緒為什麼沒有顯式呼叫這個方法,前面已經說過了。這裡來簡單的看下原始碼:static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); 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)); }
這裡首先看看Looper中的ThreadLocal中有沒有存Looper例項,如果沒有,則新建一個該執行緒的Looper例項。ThreadLocal可以看做是一個儲存執行緒私有變數的資料結構,簡單的就get和set方法,這裡不詳述了。
建立Handler
一般的用法是在主執行緒中建立Handler:
val handler = object : Handler() { override fun handleMessage(msg: Message?) { //接收到message之後執行的操作 } }
這裡如果要接收Message並執行一定的操作,就必須重寫handleMessage方法,因為Handler原始碼中這個方法是一個空方法。
/** * Subclasses must implement this to receive messages. */ public void handleMessage(Message msg) { }
然後來看一下Handler的建構函式:
public Handler() { this(null, false); } public Handler(Callback callback, boolean async) { //省略部分程式碼 //獲取當前執行緒的Looper mLooper = Looper.myLooper(); //如果當前執行緒沒有Looper,丟擲異常 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; }
從上面程式碼可以看出,如果要在一個執行緒中建立Handler,一定要先執行Looper.prepare()方法,建立一個Looper例項,不然會丟擲異常。
這裡可能會有一個疑問,我們在主執行緒中建立Handler的時候並沒有執行Looper.prepare()方法啊,為什麼可以正常執行?
答案是主執行緒其實在很早之前就已經建立了Looper例項,具體的可以參考ActivityThread這個類中的main方法,具體的這裡就不講了:
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
SamplingProfilerIntegration.start();
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
EventLogger.setReporter(new EventLoggingReporter());
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("<pre-initialized>");
//在這裡建立主執行緒Looper
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
//開始訊息佇列迴圈
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
上面程式碼中建立主執行緒的Looper,看一下具體程式碼:
/**
* 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();
}
}
正如前面所說,我們還可以傳入一個Looper例項到Handler的建構函式中,用來直接切換到主執行緒執行,或者切換到別的執行緒執行程式碼,來看看原始碼:
public Handler(Looper looper) {
this(looper, null, false);
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
就是將傳入的Looper例項賦值給mLooper。mQueue則是取得mLooper的訊息佇列。
Looper.loop()
建立完Handler例項之後,需要執行Looper.loop()方法,開始從訊息佇列中取出訊息,並分發給對應的Handler進行處理。/** * 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; for (;;) { //從訊息佇列中取出訊息 Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } //... try { //分發訊息 msg.target.dispatchMessage(msg); } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } msg.recycleUnchecked(); } }
這裡從Looper持有的訊息佇列物件中迴圈取出訊息,然後利用msg.target.dispatchMessage(msg)進行訊息分發,這裡的msg.target其實就是Handler,然後執行Handler的dispatchMesage方法.
訊息分發
看看dispatchMessage的原始碼:
/** * Handle system messages here. */ public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } } private static void handleCallback(Message message) { message.callback.run(); }
如果Message自身的callback不為空,即Message的Runnable不為空,則執行Runable,否則判斷Handler的Callback是否為空,不為空則執行Callback的handleMessage方法,再根據返回值判斷是否執行Handler自身的handleMessage方法。
總結
從一個非主執行緒建立Handler對於分析Handler的流程將會更加直觀,這裡梳理一下流程:
- 利用Looper.prepare()初始化Looper
- 建立Handler,其中Handler構造方法可以傳入相關引數實現一些特殊的功能
- 執行 Looper.loop()方法,開始從訊息佇列中取出訊息,利用Handler的dispatchMessage方法進行分發
- Handle通過post,sendMessage等方法,傳送訊息,其實就是將Message新增到MessageQueue中
- Handler通過handleMessage方法處理接收到的Message