《Android之“只是想來談談Handler機制”》
轉載請註明來自 傻小孩b(gold.xitu.io/user/57e089…喜歡的可以關注我,不定期總結文章!您的支援是我的動力哈!
前言
在談Handler的時候,應該會涉及到三個角色:Handler自身、Looper、MessageQueue。這裡我將一一做分析記錄:
Looper
Looper類程式碼並不長,所以相對消化起來並不是特別的難。
Looper是一種迴圈機制,而且保證每個執行緒只能存在一個Looper機制。
講到Looper,如果要從頭開始認識這個機制,當然要參考應用的主執行緒main函式入口,ActivityThread。程式碼:
public static void main(String[] args) {
...
Looper.prepareMainLooper();
...
Looper.loop();
}複製程式碼
再往裡面看 Looper.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();
}
}
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));
}複製程式碼
我們暫且不說這個prepareMainLooper有什麼作用,從程式碼功能,我們可以看出來,程式做了兩件事情:
1、sThreadLocal例項set了一個Looper的例項。
2、通過sThreadLocal.get() != null判斷達到不允許該執行緒中重新申請一個looper例項。
其中,ThreadLocal有點像HashMap的機制,key作為一個唯一標識值,且唯一對應一個value。所以這裡為當前執行緒例項了一個獨立標識的Looper例項。
再看Looper.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;
}
// 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) {
//...
}
msg.recycleUnchecked();
}
}
----------------------------------------------------------------------
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}複製程式碼
在上面的方法loop中,最有亮點當然就是for (;;)死迴圈了!(不然怎麼叫迴圈機制呢哈)。從程式程式碼中,我們可以發現looper機制,執行迴圈機制的開始入口,便是loop方法!但是,我們在執行之前,應該要知道是哪個執行緒的Looper例項執行了?這裡有個關鍵方法myLooper()。看到方法體的sThreadLocal.get()後,讀者會不會瞬間剛剛在哪裡判斷的時候也呼叫了。
對!就是Looper機制中的prepare方法,初始化了當前Looper的例項物件,最後通過呼叫loop()啟動了迴圈機制!大概我寫下順序
* 一般執行緒 :
Looper.prepare() --> Looper.loop()
* 主執行緒:
Looper.prepareMainLooper(); ->Looper.prepare() && 初始化sMainLooper(也就是getMainLooper獲取的主執行緒looper例項,這個動作是synchronized執行緒同步的) --> Looper.loop()複製程式碼
###MessageQueue
MessageQueue是一個訊息佇列機制,遵循先進先出的規則,簡單講述這裡由插入與刪除兩個動作,其中enqueueMessag()作為訊息插入、next()則是取出資料的時候程式會將其取出的資料移除訊息佇列,其中next是一個阻塞試的死迴圈機制,只要有訊息插入,將進行next取出。
這裡MessageQueue不做更多的說明,首先看到Looper機制是如何關聯MessageQueue列的,首先定位在Looper初始化例項的構造方法與loop啟動機制的方法
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}複製程式碼
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;
}
...
}
}複製程式碼
從程式碼結構中,可以發現訊息佇列MessageQueue,在構造方法中例項化。而且loop方法死迴圈執行中,是不斷監測佇列中next是否有訊息來臨,當然前面也說明了,next是阻塞的,因此等到有訊息message插入的時候,才會通過next獲取到message例項,然後在進行下一動作。
Handler
前面的鋪墊就是為了今天的主角,首先分析Handler之前,我們都知道日常我們的handler用法,例如如下:
private static Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 1000){
//..doing
}
}
};
handler.sendEmptyMessage(1000);複製程式碼
這裡我們直接通過構造方法到send方法再到接收原始碼進行分析
首先鎖定到構造方法
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());
}
}
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;
}複製程式碼
鎖定到重要的兩句話mLooper = Looper.myLooper();和mQueue = mLooper.mQueue;。這裡通過上面分析主執行緒的Looper機制,應該很熟悉了,構造器主要做的其中兩個重要的事情:
1、獲得當前執行緒(一般主執行緒)Looper機制例項
2、通過looper例項獲得與Looper例項繫結關係的訊息佇列例項mQueue
其次我們再看下傳送訊息,這裡我直接跳轉到最終處理訊息的邏輯:
sendEmptyMessage ->sendEmptyMessageDelayed ->sendMessageDelayed ->sendMessageAtTime->enqueueMessage複製程式碼
傳送個訊息也不容易啊~
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傳送Meesage的時候,其實最終還是呼叫了與當前執行緒的Looper例項所繫結的訊息佇列中的enqueueMessage,即只是一個插入message到訊息佇列的過渡者!當然,其中通過msg.target = this;,message物件繫結了呼叫目標是當前Handler物件。這裡我建議讀者可以手動自己看下原始碼,不難理解。
前面簡述MessageQueue的時候有提及到,MessageQueue只有兩個操作,即單連結串列的插入以及刪除。由於next()是阻塞式的死迴圈,只要訊息佇列有訊息,則馬上將message取出並溢位。
最後聯絡到真正處理轉發的Looper例項,前面也說loop方法死迴圈執行中,在不斷監測佇列中next是否有訊息來臨。如果Handler此時傳送Message到訊息佇列中,則繫結關係中的Looper例項中的loop方法將會獲得此時的message,經過訊息佇列終於來到當前執行緒的looper機制中,此時loop方法通過msg.target.dispatchMessage(msg)返回給Hander物件,這裡我再貼下loop方法
public static void loop() {
...
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
return;
}
// 這裡的target,前面我們可以在Handler看到每個Message都會通過target繫結傳送Handler物件
// 最終訊息交給Handler中dispatchMessage方法處理
msg.target.dispatchMessage(msg);
msg.recycleUnchecked();
}
}複製程式碼
// 最終處理再轉發給 handleCallback 或 handleMessage
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}複製程式碼
上面筆者直接通過註釋講述了最後的處理。當然這裡我們也可以觀察下這裡面的mCallback,這裡是我們初始化Handler的時候有構造方法可以將mCallback進行初始化,這裡我們可以觀察到在執行Handler的handleMessage前還經過mCallback.handleMessage(msg)的判斷,說明mCallback在這裡起到一個攔截的作用,如果例項者回撥返回true,就不會將訊息回撥給handleMessage。
筆者是花了三個多小時過了下原始碼和記錄了分析的筆記,當別人提起底層的原始碼機制,這塊真的有點薄弱。平常應用層開發多了,花在原始碼層分析的時間也少了,所以一有時間還是得多讀讀書,多讀讀程式碼,提升下思維和能力~共勉哈!最後我引用下別人的一張圖做最後的總結,感謝閱讀!
傻小孩b mark共勉,寫給在成長路上奮鬥的你