Handler,MessageQueue,Runnable與Looper(下)

前言

前篇對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());
  1. 那么,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ū)別。


Looper揭秘.png

這個(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)限。

  1. 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一起完成一系列操作晕粪。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末挤悉,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子巫湘,更是在濱河造成了極大的恐慌装悲,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件尚氛,死亡現(xiàn)場離奇詭異诀诊,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)阅嘶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進(jìn)店門属瓣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人讯柔,你說我怎么就攤上這事抡蛙。” “怎么了魂迄?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵粗截,是天一觀的道長。 經(jīng)常有香客問我捣炬,道長熊昌,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任湿酸,我火速辦了婚禮婿屹,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘推溃。我一直安慰自己昂利,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著页眯,像睡著了一般梯捕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上窝撵,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天,我揣著相機(jī)與錄音襟铭,去河邊找鬼碌奉。 笑死,一個(gè)胖子當(dāng)著我的面吹牛寒砖,可吹牛的內(nèi)容都是我干的赐劣。 我是一名探鬼主播,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼哩都,長吁一口氣:“原來是場噩夢啊……” “哼魁兼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起漠嵌,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤咐汞,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后儒鹿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體化撕,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年约炎,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了植阴。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,137評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡圾浅,死狀恐怖掠手,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情狸捕,我是刑警寧澤喷鸽,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站府寒,受9級特大地震影響魁衙,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜株搔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一剖淀、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧纤房,春花似錦纵隔、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽殊者。三九已至正蛙,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間芦圾,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工俄认, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留个少,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓眯杏,卻偏偏與公主長得像夜焦,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子岂贩,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評論 2 355

推薦閱讀更多精彩內(nèi)容