Handler流程分析
210329210803.png
在APP啟動(dòng)的時(shí)候,就已經(jīng)創(chuàng)建了主線程的Looper對(duì)象镰惦,在ActivityThread中的main方法中進(jìn)行創(chuàng)建犬绒。在
sendMessage()
的時(shí)候都會(huì)調(diào)用enqueueMessage()
壓入消息到消息隊(duì)列里面,主要操作就是將send的message賦值給了一個(gè)全局的message(源碼中變量名為MMessage)。在消息隊(duì)列當(dāng)中取消息的時(shí)候有個(gè)Looper.loop()
方法一直在輪尋消息礼华,按照先進(jìn)先出的原則取消息拗秘,調(diào)用了queue.next方法,該方法就是將message進(jìn)行返回聘殖,在next方法中就是將之前賦值的全局變量的mMessage再返回回來,之后調(diào)用handler里面的dispatchMessage()
方法實(shí)現(xiàn)了線程切換(如果在子線程中發(fā)送了消息)而該方法調(diào)用了handlerMessage()
并由實(shí)例化handler的時(shí)候重寫該方法取到消息奸腺。
提到了Handler大家都會(huì)聯(lián)想到另外三個(gè)對(duì)象,與Handler息息相關(guān)帮非。如圖所示
0329211822.png
在UI線程當(dāng)中使用Handler
210329212221.png
在子線程中使用Handler
0210329212350.png
注意紅框里面的代碼,為什么要在子線程中寫這兩句話呢座慰。我們知道項(xiàng)目啟動(dòng)會(huì)直接調(diào)用主線程中main方法去實(shí)例化looper對(duì)象并通過Looper.looper()激活消息并獲取消息的一系列流程版仔,而在子線程當(dāng)中如果我們直接
new Handler()
,根據(jù)threadLocalMap取looper的時(shí)候肯定是為null的蛮粮,報(bào)空指針,原因就是沒有對(duì)Looper進(jìn)行初始化然想,所以調(diào)用Looper.prepare()
方法進(jìn)行new Looper()
來看下張圖
20210329213003.png
很清楚我們可以看到無論你調(diào)用post sendMessage sendEmptyMessage 等等 最終都會(huì)調(diào)用
enqueueMessage()
將消息壓入消息隊(duì)列當(dāng)中令哟。
Handler整個(gè)流程圖
handler.png
Handler的post方法為什么是主線程
我們?cè)趯憄ost方法的時(shí)候會(huì)new一個(gè)runnable励饵,而該runerable會(huì)被封裝成一個(gè)message對(duì)象進(jìn)行發(fā)送消息。源碼顯示如下
private static Message getPostMessage(Runnable r){
Message m = Message.obtain();
m.callback = r;
return m;
}
大家來看這里面message的callback被賦值了該Runnable颓鲜,注意不是Handler實(shí)例化的時(shí)候new 的callback。這樣就通過 sendMessageDelayed(getPostMessage(r),0)
方法進(jìn)行將消息發(fā)送瘤袖,調(diào)用了dispatchMessage(Message msge)
進(jìn)行了線程的切換昂验,該方法源碼如下
public void dispatchMessage(Message msg){
if(msg.callback!=null){
handleCallback(msg);
}else{
if(mCallbackz!=null){
if{mCallback.handleMessage(msg);
return;
}
handleMessage(msge);
}
}
}
private static void handleCallback(Message message){
message.callback.run()
}
所以最終就會(huì)在主線程中執(zhí)行post方法中實(shí)現(xiàn)的run方法
小結(jié)
1.為什么主線程用Looper死循環(huán)不會(huì)引發(fā)ANR異常
因?yàn)樵贚ooper.next()開啟死循環(huán)的時(shí)候既琴,一旦需要等待時(shí)或者還沒有執(zhí)行到的時(shí)候,會(huì)調(diào)用NDK里面的JNI方法逆济,
釋放當(dāng)前時(shí)間片磺箕,這樣就不會(huì)造成ANR異常了
2.為什么Handler構(gòu)造方法里面Looper不是直接new的
如果在Handler構(gòu)造方法中new Looper松靡,怕是無法保證Looper的唯一性了,只有用Looper.prepare()才能保證唯一性雕欺,
具體看prepare方法
MessageQueue為什么要放在Looper的私有構(gòu)造方法里面初始化
因?yàn)橐粋€(gè)線程只能綁定一個(gè)Looper,所以在Looper構(gòu)造方法里面初始化可以保證mQueue也是唯一的
Thread對(duì)應(yīng)一個(gè)Looper對(duì)應(yīng)一個(gè)mQueue
主線程里面的Looper.prepare/Looper.loop,是一直在無限循環(huán)里面的嗎蛛枚?
是的
感謝
感謝大家的閱讀 點(diǎn)個(gè)贊唄
關(guān)注我 持續(xù)更新