Handler運(yùn)行機(jī)制

問(wèn)題

  1. Handler. post(Runnable r)的消息是如何傳遞的,傳遞了什么消息。
    Post內(nèi)部將r對(duì)象轉(zhuǎn)化為消息發(fā)送到Handler所在線(xiàn)程的消息隊(duì)列中,執(zhí)行Looper.loop()方法的線(xiàn)程,將直接執(zhí)行r中的run()方法(注意沒(méi)有開(kāi)啟新線(xiàn)程,而是在Handler所在的線(xiàn)程中執(zhí)行的r中的run()方法)

  2. Handler在哪個(gè)線(xiàn)程創(chuàng)建,就在哪個(gè)線(xiàn)程運(yùn)行handleMessage(Message msg)。如何實(shí)現(xiàn)的綁定。
    Looper.prepare()方法中將出初始化一個(gè)線(xiàn)程池,它能保證Looper與當(dāng)前線(xiàn)程一一對(duì)

  3. Looper的運(yùn)行機(jī)制
    Looper.prepare()方法中進(jìn)行了線(xiàn)程池的初始化、創(chuàng)建消息隊(duì)列并將消息放在消息隊(duì)列中等操作(其中msg.targe屬性保存了Handler的對(duì)象),在調(diào)用Looper.loop()方法從消息隊(duì)列中取消息并調(diào)用Handler的dispatchMessage(Message msg)方法,在該方法中執(zhí)行handleMessage(Message msg)方法處理消息

  4. 主線(xiàn)程如何利用Handler消息機(jī)制運(yùn)行handleMessage(Message msg)。
    在創(chuàng)建Handler對(duì)象的線(xiàn)程中,Looper.loop()方法調(diào)用了Handler的dispatchMessage(Message msg)方法,在該方法中執(zhí)行了handlemessage(Message msg)

  5. 主線(xiàn)程的Looper在哪定義的,handleMessage(Message msg)如何運(yùn)行到主線(xiàn)程中的。
    ActivityThread的main()方法中首先執(zhí)行了Looper.prepare()方法,并在main()方法的最后執(zhí)行了Looper.loop()方法(該方法中執(zhí)行了handleMessage(Message msg)方法)

Handler的創(chuàng)建

Handler的默認(rèn)構(gòu)造方法如下:

public Handler() {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = null;
}

在Handler中沒(méi)有看到與當(dāng)前線(xiàn)程相關(guān)的綁定操作,唯一可能與此有關(guān)的便是mLooper = Looper.myLooper();這一句。于是查看Looper.myLooper()方法,其代碼如下:

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
public static Looper myLooper() {
        return sThreadLocal.get();
}

這里主要用到的就是一個(gè)ThreadLocal,ThreadLocal是在Java.lang包里定義的一個(gè)工具類(lèi),其主要功能是為本地所有線(xiàn)程保存一個(gè)數(shù)據(jù)。ThreadLocal保證每個(gè)線(xiàn)程之間的數(shù)據(jù)相互獨(dú)立,一個(gè)線(xiàn)程相關(guān)數(shù)據(jù)的改變不會(huì)影響其他的線(xiàn)程數(shù)據(jù)。

在Looper類(lèi)中,當(dāng)ThreadLocal被有效初始化后,myLooper()方法將能夠有效的獲取當(dāng)前線(xiàn)程所對(duì)應(yīng)的Looper對(duì)象。那么Looper的靜態(tài)方法能夠通過(guò)其所執(zhí)行的線(xiàn)程方便的獲取此線(xiàn)程相關(guān)的Looper對(duì)象,使得Looper類(lèi)使用各靜態(tài)方法便能夠獲得與使用其對(duì)象一樣方便的使用方式。
ThreadLocal的初始化在Looper.prepare()中進(jìn)行。
在Looper. prepare()中將當(dāng)前線(xiàn)程相關(guān)的唯一的一個(gè)Looper對(duì)象與當(dāng)前線(xiàn)程進(jìn)行相關(guān)聯(lián),其代碼如下:

public static void prepare() {
        prepare(true);
    }

    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之前進(jìn)行prepare()(里面有ThreadLocal的初始化操作)將保證當(dāng)前線(xiàn)程擁有并只有一個(gè)Looper的對(duì)象與之相對(duì)應(yīng),在Handler創(chuàng)建時(shí)將獲取與此Handler想對(duì)應(yīng)的Looper
至此,Handler與執(zhí)行線(xiàn)程的問(wèn)題解決。

Looper的運(yùn)行

雖然解決了Handler與線(xiàn)程以及Looper之間的關(guān)聯(lián)關(guān)系,但HandleMessage()究竟如何被相關(guān)線(xiàn)程執(zhí)行的問(wèn)題仍然沒(méi)有得到解決。
在Looper的API文檔中有如下記述:
一個(gè)典型的Looper線(xiàn)程如下所示。

class LooperThread extends Thread {
      public Handler mHandler;

      public void run() {
          Looper.prepare();

          mHandler = new Handler() {
              public void handleMessage(Message msg) {
                  // process incoming messages here
              }
          };

          Looper.loop();
      }
  }

在執(zhí)行handleMessage(Message msg)線(xiàn)程中應(yīng)先執(zhí)行Looper.prepare()方法,并創(chuàng)建Handler對(duì)象,最后執(zhí)行Looper.loop()方法。因此,handleMessage(Message msg)方法應(yīng)該在Looper.loop()方法中以某種方式運(yùn)行。
Looper.loop()方法的定義如下:

public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            msg.target.dispatchMessage(msg);

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }

            msg.recycle();
        }
    }

注意:調(diào)用loop()方法的線(xiàn)程與創(chuàng)建Handler對(duì)象的線(xiàn)程是同一線(xiàn)程。或者說(shuō),Handler創(chuàng)建的線(xiàn)程在沒(méi)有改變其指向的情況下,只能由該線(xiàn)程的loop()方法來(lái)處理傳遞給Handler的消息。
在通過(guò)myLooper()方法獲取了當(dāng)前線(xiàn)程對(duì)應(yīng)的Looper對(duì)象me后,使用me獲取了當(dāng)前線(xiàn)程所對(duì)應(yīng)的MessageQueue。然后進(jìn)入了一個(gè)由for定義的死循環(huán)。
在獲取了即將要處理的Massage msg后,執(zhí)行了msg.target.dispatchMessage(msg)。

msg.target是定義在Massage中的成員變量,類(lèi)型為Handler。指代此Message應(yīng)該由哪個(gè)Handler處理。因此,當(dāng)msg.target.dispatchMessage(msg)被執(zhí)行,將執(zhí)行此Message所對(duì)應(yīng)的Handler的dispatchMessage(Message msg)方法。

Handler中dispatchMessage(Message msg)方法定義如下:

public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

在不考慮msg擁有callback的情況下,要么執(zhí)行Handler的Callback接口中的handleMessage(msg),要么執(zhí)行Handler復(fù)寫(xiě)的handleMessage(msg)。
至此,handleMessage(Message msg)方法的調(diào)用地點(diǎn)可以確定。應(yīng)該是在與Handler對(duì)象創(chuàng)建的同一線(xiàn)程中,由其后的Looper.loop()方法進(jìn)行的調(diào)用

主線(xiàn)程中的Looper

在正常編程的情況下,通常遇到的都是Activity的onCreate()方法、onStart()方法、onDestroy()方法等,他們就代表的主線(xiàn)程的使用,在這些方法中進(jìn)行UI界面的修改是被允許的,但主線(xiàn)程究竟是如何調(diào)用Looper.loop()的卻不得而知,因此需要查找Activity的實(shí)際啟動(dòng)地點(diǎn)才能得知主線(xiàn)程的真實(shí)樣貌及Looper.loop()方法調(diào)用位置。
在android.app包中查找ActivityThread類(lèi),其中在其源代碼最后定義了如下方法:

public static void main(String[] args) {
        SamplingProfilerIntegration.start();

        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        CloseGuard.setEnabled(false);

        Process.setArgV0("<pre-initialized>");

        Looper.prepareMainLooper();
        if (sMainThreadHandler == null) {
            sMainThreadHandler = new Handler();
        }

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        AsyncTask.init();

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

在看到main方法的定義后,終于看到了Activity主線(xiàn)程的真實(shí)面目。這里對(duì)Looper進(jìn)行了當(dāng)前主線(xiàn)程的prepare并且在main方法的最后執(zhí)行了Looper.loop()方法,實(shí)現(xiàn)對(duì)當(dāng)前線(xiàn)程Handler.HandleMessage(Message msg)的調(diào)用。
至此,主線(xiàn)程的Handler.HandleMessage(Message msg)的調(diào)用問(wèn)題解決。

Handler. post(Runnable r)

關(guān)于pos()t方法究竟做了些什么,怎么以一個(gè)Runnable接口當(dāng)做Message消息發(fā)送給其它線(xiàn)程以及如何執(zhí)行。這些問(wèn)題還需要查看Handler. post(Runnable r)的源代碼,其源代碼如下:

public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

根據(jù)源代碼所述,其post()方法也是通過(guò)某種形式將Runnable轉(zhuǎn)換為Message進(jìn)行消息的發(fā)送。而消息的獲取方式為getPostMessage(r),同時(shí)Runnable接口也是在這個(gè)方法中進(jìn)行處理的。getPostMessage(Runnable r)方法定義如下:

 private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

其中Message.obtain()是在消息池中獲取一條新的消息,即消息本身是空的。而Runnable接口r緊緊是將其設(shè)置給剛剛創(chuàng)建的新消息m的callback。
在獲取了消息并設(shè)置了callback后就用將這條消息send給消息隊(duì)列。這條消息的處理同樣在Looper.loop()方法中調(diào)用了Handler的dispatchMessage(Message msg)。再次給出dispatchMessage(Message msg)方法的代碼:

public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

可以看到,當(dāng)msg.callback非空時(shí),將會(huì)調(diào)用handleCallback(msg)。一下給出handleCallback(Message message)的代碼:

private static void handleCallback(Message message) {
        message.callback.run();
    }

在handleCallback(Message message)中直接調(diào)用了callback的run()方法。即在Looper.loop()方法執(zhí)行的線(xiàn)程中,直接執(zhí)行了Runnable所定義的線(xiàn)程。需要注意的是,這里并沒(méi)有啟動(dòng)新的線(xiàn)程來(lái)運(yùn)行Runnable接口,而是在Handler所在的線(xiàn)程中運(yùn)行

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,963評(píng)論 6 542
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,348評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 178,083評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,706評(píng)論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,442評(píng)論 6 412
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,802評(píng)論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,795評(píng)論 3 446
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,983評(píng)論 0 290
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,542評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,287評(píng)論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,486評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,030評(píng)論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,710評(píng)論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 35,116評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 36,412評(píng)論 1 294
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,224評(píng)論 3 398
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,462評(píng)論 2 378

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