【Android自助餐】Handler訊息機制完全解析(四)Looper解析
Android自助餐Handler訊息機制完全解析(四)Looper解析
Looper
如果你搞過Arduino,那麼你肯定知道這個loop()
方法。沒接觸過也沒關係,這個方法就是一個通過死迴圈來重複做某件事的方法。區別是Arduion的迴圈控制在loop()
Looper
的迴圈控制在loop()
方法內。這個Looper
類本身則是對這個方法做了一些封裝。
初始化prepare()
這裡標題不是構造方法,因為其構造方法被private
修飾,那麼來看看它什麼時候呼叫了構造方法。很容易就能找到private static void prepare(boolean quitAllowed)
方法,該類僅在這裡呼叫了構造方法,然而這個prepare()
也是被private
修飾的,那麼來看看這個帶參的prepare()
又在哪裡被呼叫了。結果可以找到兩個public static
修飾的方法:prepare()
和prepareMainLooper()
第一個方法原文說明如下:
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
loop()
after calling this method, and end it by callingquit()
.
某以不才為諸君翻譯如下:
作為looper初始化當前執行緒。提供一個機會來建立handler並使用looper。在使用之前,請在此方法之後呼叫
loop()
,並在結束時呼叫quit()
。
第二個方法原文說明如下:
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.
某再以不才為諸君奉上槽點:
作為looper初始化當前執行緒,並標記其為application主執行緒的looper。在application主執行緒中的looper被Android系統建立,因此開發者請永遠不要手動呼叫這個方法。
不讓掉還不讓研究麼,今天我們就看這個prepareMainLooper ()
幹了點啥。先把原始碼放上來:
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
首先呼叫了這個私有的帶參的prepare()
方法並傳了個false。這個私有方法宣告為private static void prepare(boolean quitAllowed)
,因此判斷該引數的含義是不允許退出。進來這個帶參方法看看:
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));
}
這裡出現了sThreadLocal
欄位,型別是ThreadLocal
,先來看看該類的原文說明:
Implements a thread-local storage, that is, a variable for which each thread has its own value. All threads share the same
ThreadLocal
object, but each sees a different value when accessing it, and changes made by one thread do not affect the other threads. The implementation supportsnull
values.
某以不才:
實現一個執行緒本地儲存,是什麼呢?一個讓每個執行緒都有擁有value的變數。所有的執行緒都共享同一個
ThreadLocal
物件,但每個執行緒在這拿到的value都不相同,並且一個執行緒在這裡做的改變並不影響其他執行緒。支援值為null
。
如果理解不了可以類比一下View
類,該類有個setTag()
方法,用來讓這個View攜帶附加值。此處的ThreadLocal
就是讓執行緒可以攜帶附加值,因此也有get()
和set()
方法。兩個方法中的第一句便是Thread.currentThread()
來獲取當前執行緒,所以能看出上面那句“共享同一個物件”且“各執行緒互不影響”。
回到Lopper的帶參prepare(),可以看到new了一個Lopper並放到當前執行緒的“tag”中,而此處是從prepareMainLopper()
進來的,因此當前執行緒便是主執行緒。
再看構造方法,裡面只有兩行程式碼:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
建立了訊息佇列,並獲取了當前執行緒。上文提到在Handler構造方法中有mQueue = looper.mQueue;
,說明Handler中的佇列與Lopper中的佇列是同一個佇列。
到這裡prepareMainLooper()
就執行完了,根據空參的prepare()
方法說明推斷,“Android Environment”在呼叫prepareMainLooper()
後必然會呼叫loo()
。檢視prepareMainLooper()
呼叫者可以看到,在SystemServer.run()
與ActivityThread.main()
中都在呼叫Looper.prepareMainLooper()
後不遠就呼叫了Looper.loop()
。而這兩處可以推斷一個是系統應用的主執行緒,一個是使用者應用的主執行緒。
提供looper獲取介面myLooper()
prepare()
中提到了會將looper放到執行緒儲存ThreadLocal
中,此處只需要從中取出並返回即可,因此程式碼只有一行return sThreadLocal.get();
。
處理訊息佇列loop()
先上核心原始碼:(有刪減)
for (;;) {
Message msg = queue.next();
if (msg == null) {
return;
}
msg.target.dispatchMessage(msg);
msg.recycleUnchecked();
}
這裡就一目瞭然了:一個死迴圈,不斷從佇列中取訊息並分發,如果取到null就說明訊息佇列已經退出或被釋放,此時loop終止。關於queue.next()
可以看MessageQueue的佇列管理瞭解。msg.recycleUnchecked()
可以看Message中obtain()與recycle()的來龍去脈瞭解。msg.target.dispatchMessage(msg)
中target便是在Handler.obtainMessage()
時放到訊息中的handler,dispatchMessage()
便是對訊息的處理了。