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

問(wèn)題

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

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

  3. Looper的運(yùn)行機(jī)制
    Looper.prepare()方法中進(jì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. 主線程如何利用Handler消息機(jī)制運(yùn)行handleMessage(Message msg)。
    在創(chuàng)建Handler對(duì)象的線程中踢械,Looper.loop()方法調(diào)用了Handler的dispatchMessage(Message msg)方法酗电,在該方法中執(zhí)行了handlemessage(Message msg)

  5. 主線程的Looper在哪定義的,handleMessage(Message msg)如何運(yù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)前線程相關(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è)工具類移稳,其主要功能是為本地所有線程保存一個(gè)數(shù)據(jù)蕴纳。ThreadLocal保證每個(gè)線程之間的數(shù)據(jù)相互獨(dú)立,一個(gè)線程相關(guān)數(shù)據(jù)的改變不會(huì)影響其他的線程數(shù)據(jù)个粱。

在Looper類中古毛,當(dāng)ThreadLocal被有效初始化后,myLooper()方法將能夠有效的獲取當(dāng)前線程所對(duì)應(yīng)的Looper對(duì)象都许。那么Looper的靜態(tài)方法能夠通過(guò)其所執(zhí)行的線程方便的獲取此線程相關(guān)的Looper對(duì)象稻薇,使得Looper類使用各靜態(tài)方法便能夠獲得與使用其對(duì)象一樣方便的使用方式。
ThreadLocal的初始化在Looper.prepare()中進(jìn)行胶征。
在Looper. prepare()中將當(dāng)前線程相關(guān)的唯一的一個(gè)Looper對(duì)象與當(dāng)前線程進(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)前線程擁有并只有一個(gè)Looper的對(duì)象與之相對(duì)應(yīng)睛低,在Handler創(chuàng)建時(shí)將獲取與此Handler想對(duì)應(yīng)的Looper案狠。
至此,Handler與執(zhí)行線程的問(wèn)題解決钱雷。

Looper的運(yùn)行

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

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)線程中應(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()方法的線程與創(chuàng)建Handler對(duì)象的線程是同一線程∷感或者說(shuō)婴洼,Handler創(chuàng)建的線程在沒(méi)有改變其指向的情況下,只能由該線程的loop()方法來(lái)處理傳遞給Handler的消息信夫。
在通過(guò)myLooper()方法獲取了當(dāng)前線程對(duì)應(yīng)的Looper對(duì)象me后窃蹋,使用me獲取了當(dāng)前線程所對(duì)應(yīng)的MessageQueue。然后進(jìn)入了一個(gè)由for定義的死循環(huán)静稻。
在獲取了即將要處理的Massage msg后警没,執(zhí)行了msg.target.dispatchMessage(msg)。

msg.target是定義在Massage中的成員變量振湾,類型為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ù)寫的handleMessage(msg)。
至此厦画,handleMessage(Message msg)方法的調(diào)用地點(diǎn)可以確定疮茄。應(yīng)該是在與Handler對(duì)象創(chuàng)建的同一線程中,由其后的Looper.loop()方法進(jìn)行的調(diào)用根暑。

主線程中的Looper

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

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主線程的真實(shí)面目。這里對(duì)Looper進(jìn)行了當(dāng)前主線程的prepare并且在main方法的最后執(zhí)行了Looper.loop()方法夯到,實(shí)現(xiàn)對(duì)當(dāng)前線程Handler.HandleMessage(Message msg)的調(diào)用。
至此饮亏,主線程的Handler.HandleMessage(Message msg)的調(diào)用問(wèn)題解決耍贾。

Handler. post(Runnable r)

關(guān)于pos()t方法究竟做了些什么阅爽,怎么以一個(gè)Runnable接口當(dāng)做Message消息發(fā)送給其它線程以及如何執(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í)行的線程中,直接執(zhí)行了Runnable所定義的線程鹃答。需要注意的是乎澄,這里并沒(méi)有啟動(dòng)新的線程來(lái)運(yùn)行Runnable接口,而是在Handler所在的線程中運(yùn)行测摔。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末置济,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子避咆,更是在濱河造成了極大的恐慌舟肉,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,826評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件查库,死亡現(xiàn)場(chǎng)離奇詭異路媚,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)樊销,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門整慎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人围苫,你說(shuō)我怎么就攤上這事裤园。” “怎么了剂府?”我有些...
    開封第一講書人閱讀 164,234評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵拧揽,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng)淤袜,這世上最難降的妖魔是什么痒谴? 我笑而不...
    開封第一講書人閱讀 58,562評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮铡羡,結(jié)果婚禮上积蔚,老公的妹妹穿的比我還像新娘。我一直安慰自己烦周,他們只是感情好尽爆,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,611評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著读慎,像睡著了一般漱贱。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上贪壳,一...
    開封第一講書人閱讀 51,482評(píng)論 1 302
  • 那天饱亿,我揣著相機(jī)與錄音,去河邊找鬼闰靴。 笑死彪笼,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蚂且。 我是一名探鬼主播配猫,決...
    沈念sama閱讀 40,271評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼杏死!你這毒婦竟也來(lái)了泵肄?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,166評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤淑翼,失蹤者是張志新(化名)和其女友劉穎腐巢,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體玄括,經(jīng)...
    沈念sama閱讀 45,608評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡冯丙,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,814評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了遭京。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片胃惜。...
    茶點(diǎn)故事閱讀 39,926評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖哪雕,靈堂內(nèi)的尸體忽然破棺而出船殉,到底是詐尸還是另有隱情,我是刑警寧澤斯嚎,帶...
    沈念sama閱讀 35,644評(píng)論 5 346
  • 正文 年R本政府宣布利虫,位于F島的核電站挨厚,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏列吼。R本人自食惡果不足惜幽崩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,249評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望寞钥。 院中可真熱鬧,春花似錦陌选、人聲如沸理郑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)您炉。三九已至,卻和暖如春役电,著一層夾襖步出監(jiān)牢的瞬間赚爵,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工法瑟, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留冀膝,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,063評(píng)論 3 370
  • 正文 我出身青樓霎挟,卻偏偏與公主長(zhǎng)得像窝剖,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子酥夭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,871評(píng)論 2 354

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