前言
前篇對MessageQueue痊焊、Handler等幾個(gè)概念進(jìn)行了概述囊陡,相信大家一定也有了一定的理解。接下來將對上篇遺留的問題進(jìn)行研究奏篙。由于上面中LooperThread例子只是一個(gè)殼柴淘,沒有可真正運(yùn)行的”內(nèi)容“。所以要回答剩余的問題秘通,ActivityThread是一個(gè)很好的示例为严。從名稱上看ActivityThread就是我們所熟悉的主線程。
示例
public static void main(String[] args) {
...
Looper.prepareMainLooper() ;
ActivityThread thread = new ActivityThread();
thread.attach(false);
if ( sMainThreadHandler == null){
sMainThreadHandler = thread.getHandler();
}
AsyncTask.init();
Looper,loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
如果比較上面這段代碼與LooperThread.run()的實(shí)現(xiàn)肺稀,就可以發(fā)現(xiàn)它們在整體架構(gòu)上是一樣的第股,區(qū)別主要體現(xiàn)在:
- prepareMainLooper和prepare
普通線程只要prepare就可以了;而主線程使用的是prepareMainLooper话原。 - Handler 不同
普通線程生成一個(gè)與Looper綁定的Handler對象的就行夕吻;而主線程是從當(dāng)前當(dāng)前線程中獲得的Handler(thread.getHandler());
- 那么,prepareMainLooper有什么特殊之處繁仁?
public static void prepareMainLooper() {
prepare (false); //先調(diào)用prepare
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper ();
}
}
我們可以看到涉馅,在prepareMainLooper也是需要用到prepare.參數(shù)false表示該線程不允許退出,這和前面的LooperThread不一樣黄虱,經(jīng)過prepare后稚矿,myLooper就可以得到一個(gè)本地線程<ThreadLocal>的Looper對象,然后將其賦給sMainLooper捻浦。從這個(gè)角度來講晤揣,主線程的sMainLooper其實(shí)和其他線程的Looper對象并沒有本質(zhì)的區(qū)別。
這個(gè)圖描述的是一個(gè)進(jìn)程和它內(nèi)部兩個(gè)線程的Looper情況朱灿,其中線程1是主線程昧识,線程2是普通線程。方框表示它們能訪問的范圍盗扒,如線程1就不能直接訪問到線程2中的Looper對象跪楞,但二者都可以接觸到進(jìn)程中的各元素。
線程1:因?yàn)槭荕ain Thread侣灶,它使用的是prepareMainLooper()习霹,這個(gè)函數(shù)將通過prepare()為線程1生成一個(gè)ThreadLocal的Looper對象,并讓sMainLooper指向它炫隶。這樣做的目的就是其他線程如果要獲得主線程的Looper,只需調(diào)用getMainLooper()即可。
線程2:作為普通線程阎曹,它調(diào)用的是prepare()伪阶;同時(shí)也生成一個(gè)ThreadLocal的Looper對象煞檩,只不過這個(gè)對象只能在線程內(nèi)通過myLooper()訪問。當(dāng)然栅贴,主線程內(nèi)部也可以通過這個(gè)函數(shù)訪問它的Looper對象斟湃。
由此可見,Google玩了一個(gè)技巧檐薯,從而巧妙的區(qū)分開各線程的Looper凝赛,并界定了它們的訪問權(quán)限。
- sMainThreadHandler坛缕。當(dāng)ActivityThread對象創(chuàng)建時(shí)墓猎,會在內(nèi)部同時(shí)生成一個(gè)繼承自Handler的H對象:
final H mH = new H();
ActivityThread.main中調(diào)用的tread.getHandler()返回的就是mH。
也就是說赚楚,ActivityThread提供了一個(gè)“事件管家”毙沾,以處理主線程中的各種消息。
接下來我們分析下loop()函數(shù)宠页。
public static void loop() {
final Looper me = myLooper () ;
/*loop函數(shù)也是靜態(tài)的左胞,所以它只能訪問靜態(tài)的數(shù)據(jù)。函數(shù)myLooper則調(diào)用sThreadLocal.get()來獲取與之匹配的Looper實(shí)例(其實(shí)就是取出之前prepare中創(chuàng)建那個(gè)Looper對象)*/
...
final MessageQueue queue = me.mQueue;
/*正如我們所說举户,Looper中自帶一個(gè)MessageQueue*/
for( ; ; ){//消息循環(huán)開始
Message msg = queue.next();/*從MessageQueue中取出一個(gè)消息烤宙,可能會阻塞*/
if(msg == null){
/*如果當(dāng)前消息隊(duì)列中沒有msg,說明線程要退出了俭嘁。類比于上面Windows偽代碼
例子中while判斷條件為0躺枕,這樣就會結(jié)束循環(huán)*/
return;/*消息處理完畢,進(jìn)行回收*/
}
...
msg.target.dispatchMessage(msg);
/*終于開始分派消息兄淫,重心就在這里屯远。變量target其實(shí)是一個(gè)Handler,所以
dispatchMessage最終調(diào)用的是Handler中的處理函數(shù)*/
...
msg.recycle () ;/*消息處理完畢捕虽,進(jìn)行回收*/
}
}
可以看到慨丐,loop()函數(shù)的主要工作就是不斷地從消息隊(duì)列中取出需要處理的事件,然后分發(fā)給相應(yīng)的負(fù)責(zé)人泄私。如果消息隊(duì)列為空房揭,它很可能會進(jìn)入睡眠以讓出cpu資源。而在具體事件的處理過程中晌端,程序會post新的事件到隊(duì)列中捅暴。另外,其他進(jìn)程也可能投遞新的事件到這個(gè)隊(duì)列中咧纠。APK應(yīng)用程序就會不停地執(zhí)行“處理隊(duì)列事件”的工作蓬痒,直到它退出運(yùn)行。
以上我們看到了Looper.loop的處理流程漆羔,從而知道它和前面討論的Windows消息處理機(jī)制是類似的梧奢,最后再來解決一個(gè)問題:MessageQueue是怎樣創(chuàng)建出來的狱掂?
我們有提到過,Looper中帶有唯一一個(gè)MessageQueue亲轨,是不是這樣趋惨?
/*
以下代碼還是Looper.java中的,不過只提取出MessageQueue相關(guān)的部分
*/
final MessageQueue mQueue ; /*注意它不是static的*/
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue( quitAllowed );
/*new了一個(gè)MessageQueue惦蚊,就是它了器虾。也就是說,當(dāng)Looper創(chuàng)建時(shí)蹦锋,消息隊(duì)列也同時(shí)會被創(chuàng)建出來*/
mRun = true;
mThread = Thread.currentThread();//Looper與當(dāng)前線程建立對應(yīng)關(guān)系
}
事實(shí)證明Looper內(nèi)部的確管理了一個(gè)MessageQueue兆沙,它將作為線程的消息存儲倉庫,配合Handler,Looper一起完成一系列操作晕粪。