深入理解Handler機制之引路篇

要想深入理解Handler機制,就要理解:

  • Android為何要引入Handler機制?
  • Handler機制究竟是什么?

如果去查閱官方Handler文檔,會發(fā)現(xiàn)官方?jīng)]有給Handler下過定義,更沒有介紹為何要引入Handler機制。官方文檔的第一句就說Handler允許你發(fā)送和處理與一個線程的消息隊列關(guān)聯(lián)的消息和任務(wù)。這是對Handler能力和用法的一種描述,并沒有講為什么和是什么,這就造成初學(xué)者只知道怎么用Handler,但是不知道其背后的設(shè)計思想和原理。

Android為何引入Handler

要知道Android為何引入Handler機制,首先要了解Java線程機制:

  • 線程執(zhí)行任務(wù)
  • 任務(wù)在線程的run方法中執(zhí)行
  • run方法結(jié)束,線程終止

這種機制的問題在于,創(chuàng)建線程的開銷非常大,即使通過線程池重用線程,線程調(diào)度的開銷對于數(shù)量多、密度大、要求快速響應(yīng)的UI更新任務(wù)來說也是不可接受的,于是Android引入Handler線程解決這個問題。Handler線程就是Handler機制的核心:Handler線程永不退出,在run方法中輪詢?nèi)蝿?wù),有任務(wù)就處理,沒任務(wù)就等待。

有些Handler線程是允許退出的。

Android應(yīng)用的主線程是一個不允許退出、生命周期與進程一樣長的線程。

Handler機制工作原理

Handler機制是一整套任務(wù)處理機制,需要Handler、Looper、Message和MessageQueue配合。

  • Looper在線程中死循環(huán),使線程不退出
  • Handler發(fā)送和處理Message
  • Message包含任務(wù)或者執(zhí)行任務(wù)所需的數(shù)據(jù)
  • Message發(fā)送給MessageQueue,MessageQueue負責(zé)Message的排序與出隊

Message是Android中的一個重要概念,它把Android應(yīng)用從Java的任務(wù)處理系統(tǒng)變成了消息處理系統(tǒng)。

Handler機制工作原理

Looper

要把一個線程變成Handler線程,只需在線程的run方法中調(diào)用Looper.prepare()和Looper.loop()。

class LooperThread extends Thread {
    public void run() {
        Looper.prepare();
        Looper.loop();
    }
}

Looper的創(chuàng)建非常巧妙,是在Looper.prepare()中完成的。Looper實例存儲在ThreadLocal中,可以通過Looper.myLooper()獲取。Looper默認是可以退出的,要創(chuàng)建不可退出的Looper,需要調(diào)用Looper.prepare(false),主線程Looper正是這樣創(chuàng)建的。要注意的是,一個線程只能與一個Looper關(guān)聯(lián)。

Looper創(chuàng)建后通過Looper.loop()完成工作。正如上文所說,Looper.loop()在Thread.run方法中輪詢消息,使線程不退出、消息串行處理。

// Looper.java
public static void loop() {
    for (;;) {
        Message msg = queue.next(); // might block
        msg.target.dispatchMessage(msg);
    }
}

MessageQueue

MessageQueue是在Looper的構(gòu)造函數(shù)內(nèi)創(chuàng)建的。由此可知,線程、Looper和MessageQueue三者是一一對應(yīng)的。

MessageQueue的主要工作是處理Message入列和安排Message出列。MessageQueue并不是常規(guī)的先進先出隊列,而是根據(jù)Message期望執(zhí)行的時間排列的。

  • 入列:boolean enqueueMessage(Message msg, long when)
  • 出列:Message next()

這兩個方法都有用到native代碼,實現(xiàn)比較復(fù)雜,就不分析了(因為沒看懂??)。

Message

Message是一個數(shù)據(jù)類,攜帶要執(zhí)行的任務(wù)(Runnable)或執(zhí)行任務(wù)所需的數(shù)據(jù)(what、arg1、arg2、obj、data)以及任務(wù)處理者(Handler)和任務(wù)執(zhí)行時間(when)。Message的實現(xiàn)是一個單鏈表結(jié)點,因此可以攜帶下一條Message。MessageQueue正是基于Message單鏈表實現(xiàn)的。

由于是數(shù)據(jù)類,且應(yīng)用中需要大量Message,因此Message實現(xiàn)了重用緩存池,通過Message.obtain()獲取。與MessageQueue一樣,Message緩存池也是基于其單鏈表實現(xiàn)的,不同的是緩存池是棧而非隊列。

Handler

官方文檔說,Handler與創(chuàng)建它的線程及該線程的MessageQueue關(guān)聯(lián)(When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it)。這說的是不指定Looper的情況下,Handler自動與該線程的Looper關(guān)聯(lián),上文說,線程、Looper和MessageQueue三者一一對應(yīng),因此默認情況下,Handler與創(chuàng)建它的線程及該線程的MessageQueue關(guān)聯(lián)。但在創(chuàng)建Handler時是可以指定Looper的,這樣就會與Looper所在的線程和MessageQueue關(guān)聯(lián)。

不管Handler在哪個線程創(chuàng)建,它發(fā)出的消息都是在與之關(guān)聯(lián)的Looper所在的線程處理的,這正是Handler用于線程間通信的原理。由于后臺線程不允許更新UI,因此后臺線程通過一個與主線程Looper關(guān)聯(lián)的Handler(new Handler(Looper.getMainLooper()))來更新UI。

Handler發(fā)送消息有兩種方式:

  • sendMessage(Message)
  • post(Runnable)

但其實就一種,因為Runnable也會轉(zhuǎn)為Message。Handler發(fā)送消息還可以設(shè)置延時或定時,但最終也是一種,因為延時也會被轉(zhuǎn)為定時任務(wù)。因此所有的發(fā)送方式最終都調(diào)用下列方法處理:

// Handler.java
public boolean sendMessageAtTime(Message msg, long uptimeMillis)

放入MessageQueue中的Message會被逐個取出,串行處理。有3處對象可以處理消息:

  1. Message自身callback(一般是post(Runnable)方式發(fā)送的消息)
  2. Handler創(chuàng)建時設(shè)置的mCallback
  3. Handler
// Handler.java
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

HandlerThread

Android應(yīng)用的大部分消息都可以由主線程處理,如果需要在后臺線程中處理消息,可以創(chuàng)建一個新的Handler線程。

開發(fā)者并不需要自己實現(xiàn)Handler線程,直接使用內(nèi)置的HandlerThread即可:

HandlerThread handlerThread = new HandlerThread("MyHandlerThread");
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper());

源碼篇和實現(xiàn)篇

要想深入理解Handler機制,光憑一篇引路文章是不夠的,更重要的是閱讀源碼、動手實踐和總結(jié)分享。

是不是很期待源碼篇和實踐篇?你來寫!

看好你哦~

拓展閱讀

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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