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