Handler,MessageQueue,Runnable與Looper(下)

前言

前篇對(duì)MessageQueue、Handler等幾個(gè)概念進(jìn)行了概述,相信大家一定也有了一定的理解。接下來(lái)將對(duì)上篇遺留的問(wèn)題進(jìn)行研究。由于上面中LooperThread例子只是一個(gè)殼,沒(méi)有可真正運(yùn)行的”內(nèi)容“。所以要回答剩余的問(wèn)題,ActivityThread是一個(gè)很好的示例。從名稱(chēng)上看ActivityThread就是我們所熟悉的主線(xiàn)程。

示例

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)它們?cè)谡w架構(gòu)上是一樣的,區(qū)別主要體現(xiàn)在:

  • prepareMainLooper和prepare
    普通線(xiàn)程只要prepare就可以了;而主線(xiàn)程使用的是prepareMainLooper。
  • Handler 不同
    普通線(xiàn)程生成一個(gè)與Looper綁定的Handler對(duì)象的就行;而主線(xiàn)程是從當(dāng)前當(dāng)前線(xiàn)程中獲得的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表示該線(xiàn)程不允許退出,這和前面的LooperThread不一樣,經(jīng)過(guò)prepare后,myLooper就可以得到一個(gè)本地線(xiàn)程<ThreadLocal>的Looper對(duì)象,然后將其賦給sMainLooper。從這個(gè)角度來(lái)講,主線(xiàn)程的sMainLooper其實(shí)和其他線(xiàn)程的Looper對(duì)象并沒(méi)有本質(zhì)的區(qū)別。


Looper揭秘.png

這個(gè)圖描述的是一個(gè)進(jìn)程和它內(nèi)部?jī)蓚€(gè)線(xiàn)程的Looper情況,其中線(xiàn)程1是主線(xiàn)程,線(xiàn)程2是普通線(xiàn)程。方框表示它們能訪(fǎng)問(wèn)的范圍,如線(xiàn)程1就不能直接訪(fǎng)問(wèn)到線(xiàn)程2中的Looper對(duì)象,但二者都可以接觸到進(jìn)程中的各元素。
線(xiàn)程1:因?yàn)槭荕ain Thread,它使用的是prepareMainLooper(),這個(gè)函數(shù)將通過(guò)prepare()為線(xiàn)程1生成一個(gè)ThreadLocal的Looper對(duì)象,并讓sMainLooper指向它。這樣做的目的就是其他線(xiàn)程如果要獲得主線(xiàn)程的Looper,只需調(diào)用getMainLooper()即可。
線(xiàn)程2:作為普通線(xiàn)程,它調(diào)用的是prepare();同時(shí)也生成一個(gè)ThreadLocal的Looper對(duì)象,只不過(guò)這個(gè)對(duì)象只能在線(xiàn)程內(nèi)通過(guò)myLooper()訪(fǎng)問(wèn)。當(dāng)然,主線(xiàn)程內(nèi)部也可以通過(guò)這個(gè)函數(shù)訪(fǎng)問(wèn)它的Looper對(duì)象。
由此可見(jiàn),Google玩了一個(gè)技巧,從而巧妙的區(qū)分開(kāi)各線(xiàn)程的Looper,并界定了它們的訪(fǎng)問(wèn)權(quán)限。

  1. sMainThreadHandler。當(dāng)ActivityThread對(duì)象創(chuàng)建時(shí),會(huì)在內(nèi)部同時(shí)生成一個(gè)繼承自Handler的H對(duì)象:
    final H mH = new H();
    ActivityThread.main中調(diào)用的tread.getHandler()返回的就是mH。
    也就是說(shuō),ActivityThread提供了一個(gè)“事件管家”,以處理主線(xiàn)程中的各種消息。
    接下來(lái)我們分析下loop()函數(shù)。
public static void loop() {
         final Looper me = myLooper () ;
         /*loop函數(shù)也是靜態(tài)的,所以它只能訪(fǎng)問(wèn)靜態(tài)的數(shù)據(jù)。函數(shù)myLooper則調(diào)用sThreadLocal.get()來(lái)獲取與之匹配的Looper實(shí)例(其實(shí)就是取出之前prepare中創(chuàng)建那個(gè)Looper對(duì)象)*/
         ...
         final MessageQueue queue = me.mQueue;
         /*正如我們所說(shuō),Looper中自帶一個(gè)MessageQueue*/
         for( ; ; ){//消息循環(huán)開(kāi)始
                 Message msg = queue.next();/*從MessageQueue中取出一個(gè)消息,可能會(huì)阻塞*/
                 if(msg == null){
                  /*如果當(dāng)前消息隊(duì)列中沒(méi)有msg,說(shuō)明線(xiàn)程要退出了。類(lèi)比于上面Windows偽代碼
                  例子中while判斷條件為0,這樣就會(huì)結(jié)束循環(huán)*/
           return;/*消息處理完畢,進(jìn)行回收*/
                  }
          ...
          msg.target.dispatchMessage(msg);
                  /*終于開(kāi)始分派消息,重心就在這里。變量target其實(shí)是一個(gè)Handler,所以
                  dispatchMessage最終調(diào)用的是Handler中的處理函數(shù)*/
          ...
                  msg.recycle () ;/*消息處理完畢,進(jìn)行回收*/
         }
}

可以看到,loop()函數(shù)的主要工作就是不斷地從消息隊(duì)列中取出需要處理的事件,然后分發(fā)給相應(yīng)的負(fù)責(zé)人。如果消息隊(duì)列為空,它很可能會(huì)進(jìn)入睡眠以讓出cpu資源。而在具體事件的處理過(guò)程中,程序會(huì)post新的事件到隊(duì)列中。另外,其他進(jìn)程也可能投遞新的事件到這個(gè)隊(duì)列中。APK應(yīng)用程序就會(huì)不停地執(zhí)行“處理隊(duì)列事件”的工作,直到它退出運(yùn)行。
以上我們看到了Looper.loop的處理流程,從而知道它和前面討論的Windows消息處理機(jī)制是類(lèi)似的,最后再來(lái)解決一個(gè)問(wèn)題:MessageQueue是怎樣創(chuàng)建出來(lái)的?
我們有提到過(guò),Looper中帶有唯一一個(gè)MessageQueue,是不是這樣?

/*
以下代碼還是Looper.java中的,不過(guò)只提取出MessageQueue相關(guān)的部分
*/
final MessageQueue mQueue ; /*注意它不是static的*/
private Looper(boolean quitAllowed) {
     mQueue = new MessageQueue( quitAllowed );
     /*new了一個(gè)MessageQueue,就是它了。也就是說(shuō),當(dāng)Looper創(chuàng)建時(shí),消息隊(duì)列也同時(shí)會(huì)被創(chuàng)建出來(lái)*/
     mRun = true;
     mThread = Thread.currentThread();//Looper與當(dāng)前線(xiàn)程建立對(duì)應(yīng)關(guān)系
     }

事實(shí)證明Looper內(nèi)部的確管理了一個(gè)MessageQueue,它將作為線(xiàn)程的消息存儲(chǔ)倉(cāng)庫(kù),配合Handler,Looper一起完成一系列操作。

?著作權(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)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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