Handler、Looper、message、MessageQueue源碼解析

最近在寫一篇內(nèi)存泄漏的博客,還在完善中,其中寫到handler引起的內(nèi)存泄漏,發(fā)現(xiàn)對(duì)Handler了解太過(guò)狹隘,于是百度搜索大神對(duì)Handler的剖析,打算站在他們的肩膀上領(lǐng)略一下Handler相關(guān)的源碼。廢話不多說(shuō),趕緊上代碼。

參考鏈接:https://halfstackdeveloper.github.io/2016/08/31/Android-Handler-%E5%8E%9F%E7%90%86%E5%88%86%E6%9E%90/

https://www.zhihu.com/question/34652589

http://blog.csdn.net/sdkfjksf/article/details/52777722

深入源碼

Handler的使用

先來(lái)回想一下我們平時(shí)都是怎么使用Handler的。

step1:初始化一個(gè)Handler對(duì)象,重寫其handleMessage()方法:

Handler mHandler=new Handler() {

@Override

public void handleMessage(Message msg){

//todo

}

};

step2:發(fā)送消息通知Handler處理:

Message msg=mHandler.obtainMessage();

msg.what=0;

mHandler.sendMessage(msg);

我們經(jīng)常會(huì)在主線程中進(jìn)行step1操作,而在子線程中通過(guò)setp2來(lái)與主現(xiàn)場(chǎng)通信,如執(zhí)行UI操作。那么Handler究竟是如何做到線程之間通信的呢?故事要從一個(gè)叫Looper的家伙說(shuō)起。

Looper是什么

在Android中,對(duì)于每一個(gè)線程,都可以創(chuàng)建一個(gè)Looper對(duì)象(最多一個(gè)?。┖投鄠€(gè)Handler。

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就像一個(gè)消息泵,源源不斷的從消息池中拿到消息,交給Handler處理。

我們先來(lái)簡(jiǎn)單的看一下Looper類,Looper類中有有四個(gè)我們必須要了解的變量:

private static final ThreadLocal sThreadLocal = new ThreadLocal();

private static Looper sMainLooper;? // guarded by Looper.class

final MessageQueue mQueue;

final Thread mThread;

上面定義了兩個(gè)靜態(tài)變量:一個(gè)ThreadLocal類型的變量sThreadLocal,一個(gè)Looper類型的變量sMainLooper。還有兩個(gè)final變量:一個(gè)隊(duì)列類型的mQueue,一個(gè)線程類型的mThread。這四個(gè)變量至關(guān)重要。

一個(gè)線程想要使用Handler,就必須得創(chuàng)建一個(gè)Looper對(duì)象。那么創(chuàng)建一個(gè)Looper對(duì)象需要做什么呢?很簡(jiǎn)單,一行代碼足以。(main線程中不需要?jiǎng)?chuàng)建Looper對(duì)象,因?yàn)樵贏vtivityThread.java類中的mian方法里已經(jīng)創(chuàng)建了Looper對(duì)象,相關(guān)代碼也是只有一行Looper.prepareMainLooper();)

Looper.prepare();

//非UI線程初始化Handler必須加此代碼否則報(bào)異常: RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");

待handler.sendMsg();執(zhí)行后,還有一句關(guān)鍵代碼就是:Looper.loop();

源碼請(qǐng)看:

在Looper.prepare()中,Looper類會(huì)創(chuàng)建一個(gè)新的Looper對(duì)象,并放入全局的sThreadLocal中。

sThreadLocal.set(new Looper(quitAllowed));

我們?cè)倮^續(xù)深入看看new Looper(quitAllowed),很簡(jiǎn)單,也就兩行代碼:

mQueue = new MessageQueue(quitAllowed);mThread = Thread.currentThread();

原來(lái)只是初始化mQueue和mThread這兩個(gè)變量。

但是僅僅創(chuàng)建了Looper還不行,還必須開(kāi)啟消息循環(huán),要不然要Looper有何用。開(kāi)啟消息循環(huán)同樣很簡(jiǎn)單:

Looper.Loop();

現(xiàn)在再來(lái)看一看Looper.loop(),部分源碼已省略:

public static void loop() {

final Looper me = myLooper();

//下面是Looper.myLooper()源碼

public static @Nullable Looper myLooper() {

return sThreadLocal.get();//通過(guò)Looper對(duì)象攜帶的線程名稱得到Looper對(duì)象(線程名稱唯一)

}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返回的是Handler對(duì)象,這里的dispatchMessage(msg)相當(dāng)于回調(diào)給handler讓其處理該msgmsg.target.dispatchMessage(msg);

......//回收消息資源msg.recycleUnchecked();

}

}

這段代碼看起來(lái)一堆,其實(shí)主要工作也就在于那個(gè)for循環(huán)。不過(guò)還是從第一行代碼先看起吧。

第一行調(diào)用了函數(shù)myLooper(),這個(gè)函數(shù)的作用在于從sThreadLocal中取出當(dāng)前線程的Looper對(duì)象,因?yàn)閟ThreadLocal為ThreadLocal類型,所以它會(huì)保證在多線程情景下,每個(gè)線程的數(shù)據(jù)互不干擾,只能取出自己的Looper對(duì)象。

接下來(lái)取出Looper對(duì)象中的mQueue變量。

final MessageQueue queue = me.mQueue;

再來(lái)看看for循環(huán),大家可以發(fā)現(xiàn)這是一個(gè)無(wú)限循環(huán),沒(méi)有終止條件。正是這個(gè)for循環(huán),開(kāi)啟了我們的消息機(jī)制的循環(huán),源源不斷的將消息給發(fā)送出去:

Message msg = queue.next();

msg.target.dispatchMessage(msg);

如何終止該循環(huán)呢?(在下面的Looper中會(huì)詳細(xì)介紹)

Handler與Looper的綁定

說(shuō)了這么多,大家對(duì)Looper應(yīng)該稍微有點(diǎn)了解了,但是上述的代碼里似乎沒(méi)有涉及到我們使用的Handler啊,那Looper是如何與Handler進(jìn)行綁定的呢?又是怎么拿到我們的消息并進(jìn)行分發(fā)的呢?這就要看看Handler的源碼了。

先要看一看Handler的構(gòu)造函數(shù):

public Handler(Callback callback, boolean async) {

......

mLooper = Looper.myLooper();

if (mLooper == null) {

throw new RuntimeException(

"Can't create handler inside thread that has not called Looper.prepare()");

}

mQueue = mLooper.mQueue;

......

}

這里主要有兩步操作:首先是獲取當(dāng)前線程的Looper對(duì)象,賦值給本地變量,接著將Looper中的消息隊(duì)列mQueue賦值給Handler的mQueue。通過(guò)這兩步,Handler就與當(dāng)前線程的Looper對(duì)象綁定了。

再回到Handler的日常使用:

handler.sendEmptyMessage(int)

我們直接深入到這個(gè)方法的最底層:sendMessageAtTime(Message msg,long uptimeMills);

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);

}

先是將消息隊(duì)列mQueue賦值給局部變量queue(注意注意!這個(gè)mQueue指向的可是Looper的mQueue,忘記了請(qǐng)看前述Hander的構(gòu)造函數(shù))。再直接看最后一句,enqueueMessage(queue, msg, uptimeMillis);這里應(yīng)該是將我們發(fā)送的消息入隊(duì)了。再向下挖:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {

msg.target = this;

if (mAsynchronous) {

msg.setAsynchronous(true);

}

return queue.enqueueMessage(msg, uptimeMillis);

}

果然,這里是將msg放入了Looper的mQueue中了。好了,消息放到隊(duì)列中去了,有進(jìn)就有出啊,你個(gè)送信的總得把信送出去吧。還記得Loop.loop()方法嗎,我們前面說(shuō)該方法開(kāi)起來(lái)消息機(jī)制的循環(huán)。loop()的for循環(huán)中最終調(diào)用了msg.target.dispatchMessage(msg);而msg.target, 指向的就是消息的收信人,也就是Handler,那么我們又回到了Handler的源碼:

public void dispatchMessage(Message msg) {

if (msg.callback != null) {

handleCallback(msg);

} else {

if (mCallback != null) {

if (mCallback.handleMessage(msg)) {

return;

}

}

handleMessage(msg);

}

}

哈哈,看見(jiàn)了啥?handleMessage(msg) 啊。我們平時(shí)new Hander時(shí)咋寫的記得吧。

Handler mHandler=new Handler() {

@Override

public void handleMessage(Message msg){

//todo

}

};

我們重寫了handleMessage方法,處理我們接收到的消息,OK,消息終于送達(dá)到目的地了!

主線程的Looper

最后在說(shuō)一點(diǎn),我們平時(shí)開(kāi)發(fā)過(guò)程中,并沒(méi)有在Activity中去初始化Looper,那為什么可以使用Handler呢?其實(shí)Activity所在的主線程照樣也創(chuàng)建了Looper對(duì)象,替你干了活而已,雖然它是主線程,也得照樣按照規(guī)則辦事啊。

Activity所在的主線程是ActivityThread,其實(shí)這樣說(shuō)并不準(zhǔn)確,因?yàn)锳ctivityThread并不是一個(gè)線程,它只是主線程的一個(gè)入口:ActivityThread中的void main(String[] args)。

public static void main(String[] args) {

......

Looper.prepareMainLooper();

.....

Looper.loop();

.....

}

我擦,你看它已經(jīng)默默做好了一切!

細(xì)心的你應(yīng)該發(fā)現(xiàn)了,這里調(diào)用的是Looper.prepareMainLooper(),而不是之前所說(shuō)的Looper.prepare()。是啊,它可是主線程,總得有點(diǎn)不一樣的地方吧,豈能完全平起平坐。

我們來(lái)挖一挖這個(gè)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();

}

}

它首先也調(diào)用了prepare(false)方法,只不過(guò)又多做了一件事:sMainLooper = myLooper();

這時(shí)你要問(wèn)了,這個(gè)變量有啥用?。看蠹疫€記得這個(gè)變量是靜態(tài)的吧,這樣在子線程中,你就可以通過(guò)getMainLooper()來(lái)獲得主線程的Looper對(duì)象了。而對(duì)于其他的子線程,因?yàn)樗鼈兊腖ooper對(duì)象只存儲(chǔ)在sThreadLocal中,所以只能夠取出自己的Looper對(duì)象了。

舉個(gè)例子,子線程想要在主線程中執(zhí)行一段代碼,就可以按照如下操作:

new Handler(getMainLooper()).post(new Runnable() {

@Override

public void run() {

//todo

}

})

最后的福利--Looper.loop()無(wú)線循環(huán)

對(duì)于線程既然是一段可執(zhí)行的代碼,當(dāng)可執(zhí)行代碼執(zhí)行完成后,線程生命周期便該終止了,線程退出。而對(duì)于主線程,我們是絕不希望會(huì)被運(yùn)行一段時(shí)間,自己就退出,那么如何保證能一直存活呢?簡(jiǎn)單做法就是可執(zhí)行代碼是能一直執(zhí)行下去的,死循環(huán)便能保證不會(huì)被退出,例如,binder線程也是采用死循環(huán)的方法,通過(guò)循環(huán)方式不同與Binder驅(qū)動(dòng)進(jìn)行讀寫操作,當(dāng)然并非簡(jiǎn)單地死循環(huán),無(wú)消息時(shí)會(huì)休眠。但這里可能又引發(fā)了另一個(gè)問(wèn)題,既然是死循環(huán)又如何去處理其他事務(wù)呢?通過(guò)創(chuàng)建新線程的方式。

真正會(huì)卡死主線程的操作是在回調(diào)方法onCreate/onStart/onResume等操作時(shí)間過(guò)長(zhǎng),會(huì)導(dǎo)致掉幀,甚至發(fā)生ANR,looper.loop本身不會(huì)導(dǎo)致應(yīng)用卡死。

相關(guān)鏈接:https://www.zhihu.com/question/34652589/answer/90344494

總結(jié)

以上就是Handler與Looper的哀怨情仇??偟脕?lái)說(shuō),Looper就像是一個(gè)郵局,Handler通過(guò)sendMessage()將信件交給Looper,放到郵局的倉(cāng)庫(kù)mQueue里,郵局Looper再不斷的從倉(cāng)庫(kù)中取出信,交還給信對(duì)應(yīng)的Handler,收信人調(diào)用handleMessage()來(lái)讀信。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容