導(dǎo)讀
- 移動開發(fā)知識體系總章(Java基礎(chǔ)、Android、Flutter)
- Android Handler消息機(jī)制
- Android中為什么主線程不會因?yàn)長ooper.loop里的無限循環(huán)ANR?
- ActivityThread工作原理
由于我更新優(yōu)化本篇文章中的筆誤之處,導(dǎo)致文章莫名被刪除,故此重新發(fā)布!
Android Handler消息機(jī)制
為了避免ANR,我們通常會把耗時操作放在子線程里面去執(zhí)行,因?yàn)樽泳€程不能更新UI,所以當(dāng)子線程需要更新UI的時候就需要借助到Andriod的消息機(jī)制,也就是Handler機(jī)制了。那么其原理是什么呢?下面我們根據(jù)平時的使用一步一步來解讀源碼。
以上內(nèi)容可能會產(chǎn)生的問題:
- 什么是ANR?
在Android上,如果你的應(yīng)用程序有一段時間無法響應(yīng),系統(tǒng)會向用戶顯示一個對話框,這個對話框稱作應(yīng)用程序無響應(yīng)(ANR:Application Not Responding)對話框。用戶可以選擇讓程序繼續(xù)運(yùn)行,但是,他們在使用你的應(yīng)用程序時,并不希望每次都要處理這個對話框。因此,在程序里對響應(yīng)性能的設(shè)計(jì)很重要,這樣,系統(tǒng)不會顯示ANR給用戶。
- 耗時操作為什么不能再主線程?
- 為什么子線程不能更新UI?
- Handler機(jī)制是什么?
一. Handler的常見用法。
- 創(chuàng)建handler對象并重寫handleMessage方法,以便于處理自己需要的邏輯。
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg){
...
}
}
- 使用handler 對象發(fā)送消息(對象、載體)。
private void sendMsg(){
Mssages msg = handler.obtainMessage();
msg.what=what;
msg.age1=age1;
handler.sendMessageDelayed(msg,1000);
}
...
二. 代碼分析
- 以上代碼可以看出,在主線程創(chuàng)建了一個handler對象,并重構(gòu)了handleMessage方法,然后通過handler發(fā)送消息對象,中間經(jīng)過一系列處理后,handleMessage方法接收到傳遞的數(shù)據(jù),就可以處理具體的邏輯了(如更新UI)。
- 以上代碼可以看出,發(fā)送消息、最后處理數(shù)據(jù)時都使用的是Message對象,這是在源碼設(shè)計(jì)層面時約定的媒介(載體、中間件、快遞),這里先不進(jìn)行展開,只做一個簡單的介紹:
what字段作為標(biāo)識,
arg1/arg2字段作為簡單類型的參數(shù),
obj字段作為復(fù)雜對象,以及Bundle常見參數(shù)
target字段作為handler對象標(biāo)識
- 再來看發(fā)送消息 sendMessageDelayed,通過源碼可以發(fā)現(xiàn)所有的sendMessage方法執(zhí)行后,最終都會走sendMessageAtTime()方法(這里就不貼上具體源碼了)。
public boolean sendMessageAtTime(@NonNull 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);
}
在這個sendMessageAtTime方法里會拿到一個MessageQueue的實(shí)例對象,并觸發(fā)enqueueMessage(queue, msg, uptimeMillis)方法。那我們就先看看enqueueMessage方法,然后在去看mQueue怎么來的。
boolean enqueueMessage(Message msg, long when) {
...
Message p = mMessages;
synchronized (this) {
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
...
Message p = mMessages;
...
msg.next = p; // invariant: p == prev.next
...
}
}
return true;
}
首先這是一個被synchronized修飾的同步代碼塊,確保了并發(fā)問題;核心邏輯是對Message的實(shí)例對象p進(jìn)行非空判斷,最終兩個兩個邏輯里都會把msg放到Message實(shí)例對象p的next方法中進(jìn)行排隊(duì)。
4.現(xiàn)在回來研究這個mQueue對象是怎么來的呢,通過command+f(ctrl+f)搜索,最終發(fā)現(xiàn)是Handler構(gòu)造方法中獲取的(通過代碼可以看出mQueue的獲取是通過looper.mQueue,而我們前面在創(chuàng)建handler的時候使用的是無參構(gòu)造的,那么這個Looper對象的實(shí)例額mLooper是怎么來的?)
@UnsupportedAppUsage
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
4.1 觀察Handler的其他重載的構(gòu)造方法發(fā)現(xiàn),我們的無參構(gòu)造最后會走如下構(gòu)造,而這個mLooper實(shí)例對象是通過Looper.myLooper()方法獲取的而且還不能為空!由此可以看出Looper、MessageQueue都是非常重要的對象。
public Handler(@Nullable Callback callback, boolean async) {
...
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
}
4.2 繼續(xù)跟蹤源碼,進(jìn)入Looper類發(fā)現(xiàn)myLooper()的內(nèi)部是通過sThreadLocal.get()獲取的,那么有g(shù)et()自然是有set()的,所以我們command+f(ctrl+f)搜索找到了prepare()方法,而這個方法會被prepareMainLooper的方法執(zhí)行,prepare翻譯過來是準(zhǔn)備的意思,prepareMainLooper就是準(zhǔn)備主Looper的意思了,這里要注意有synchronized修飾噢。
...
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
...
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));
}
...
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
4.3 到這里,我們知道了Handler無參構(gòu)造的時候其實(shí)是使用了一個叫MainLooper及MessageQueue的對象,那這個MainLooper具體又是在哪里創(chuàng)建的呢,那就查查這個prepareMainLooper()方法在哪里被調(diào)用的使用command+shift+f進(jìn)行全局搜索:
搜索出來4個結(jié)果,除了Looper.java外,SystemServer.java是系統(tǒng)級別的這里不做深入,后續(xù)新增該類的解讀; Bridge.java是橋梁類,注釋中寫著“Main entry point of the LayoutLib Bridge.”,這里也不做深入;最后是ActivityThread.java類,也是這里需要重點(diǎn)關(guān)注的類:
public final class ActivityThread extends ClientTransactionHandler {
...
public static void main(String[] args) {
...
Looper.prepareMainLooper();
...
Looper.loop();
...
}
...
}
有兩個重大發(fā)現(xiàn):
1、Looper.prepareMainLooper()是在一個叫ActivityThread 的 final 類型的Java類,的main(String[] args) 方法中執(zhí)行的。
2、還執(zhí)行了Looper.loop();
main是Java的入口方法,說明什么呢,在整個(應(yīng)用)代碼里L(fēng)ooper.prepareMainLooper()是最先執(zhí)行的代碼之一,也就是說Handler所使用的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;
}
...
msg.target.dispatchMessage(msg);
...
msg.recycleUnchecked();
}
}
這里可以看出幾個重要消息
1、Looper、MessageQueue是不可缺少的對象
2、使用到了for (;;) ,就是說無限循環(huán),一直去MessageQueue的next()去查有沒有Message對象,
3、有Message對象時會執(zhí)行msg.target.dispatchMessage(msg);方法
當(dāng)然也會有2個困惑的問題
3.1、for (;;) 這個無限循環(huán)不會讓APP卡死嗎?
3.2、 有一行代碼if (msg == null) return 其注釋是的意思是是阻塞,return之后不是繼續(xù)for循環(huán)嗎?
4.4 繼續(xù)跟蹤 msg.target.dispatchMessage(msg)方法,前面有提到target其實(shí)就是Handler(為什么呢,大家有興趣可以看看源碼)也就是說dispatchMessage方法是Handler類下的
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
發(fā)現(xiàn)這里調(diào)用了一個很眼熟的方法名handleMessage 沒錯,這就是一開始我們重寫的的handleMessage方法,至此,腦海中是否已經(jīng)有了一個Handle整體輪廓圖?是否還有一個疑問,Handler啥時候把線程給切換了?
總結(jié)
1.APP在啟動的時候,最先啟動ActivityThread類的main方法,其中Looper的初始化工作和準(zhǔn)備工作就是這時候執(zhí)行的(獲得Looper對象、MessageQueue)
2.Looper開啟loop()方法(永動機(jī))去死循環(huán)遍歷MessageQueue的消息隊(duì)列(至于為什么沒有卡死前面有提到?有提到嗎,為什么我修改的時候發(fā)現(xiàn)沒有,捂臉)
3.在通常用法中創(chuàng)建Handler對象時,構(gòu)造方法會拿到looper、mQueue對象
4.使用Handler對象發(fā)送消息(sendXxx())
5.把消息加入隊(duì)列(mQueue.enqueueMessage)
6.第2步的永動機(jī)讀取MessageQueue的消息
7.執(zhí)行dispatchMessage方法
8.執(zhí)行代碼中創(chuàng)建Handler方法時重寫的handleMessage方法完成Handler機(jī)制的整個過程。
最后,讓我們帶著文章中的問題,進(jìn)行繼續(xù)深入:Android中為什么主線程不會因?yàn)長ooper.loop里的無限循環(huán)ANR?