Handler總結

本文主要是梳理一下 Handler 的相關知識,不會每個點都講的很細,關鍵在于每個知識點的融匯貫通。

一、Android 消息機制工作的幾點要素:

1、消息循環(huán) Looper
2、消息隊列 MessageQueue
3、發(fā)送消息、處理消息 Handler
4、消息實體 Message

Handler 是每個 Android 開發(fā)者在一開始就會接觸到的,上面提到的幾點要素也是我們牢記在心的,但其實在一開始我沒有去好好讀源碼的情況下,雖然記住了這幾點,但根本沒理解消息機制,只是會用sendMessage去發(fā)消息,handleMessage去處理消息,后來我也陸陸續(xù)續(xù)讀了幾次 Handler 的源碼,直到最近我想整理下消息機制的內(nèi)容,我才真正體會到消息機制的設計思想,并且把消息機制和一些我們不是經(jīng)常使用的工具,比如IntentService、HandlerThreadLocalBroadcastManager所聯(lián)系起來,讓我深刻的感受到消息機制在 Android 系統(tǒng)中的無處不在。在我看來,要理解消息機制,一定要理清楚線程的概念。下面我們就從先來看一下主線程(UI 線程)中的消息機制。
當應用程序啟動初始化進程時,會加載ActivityThread 這個類,來看一下main函數(shù)中是如何初始化主線程中的消息機制的:

public static void main(String[] args) {

    ...

    Looper.prepareMainLooper();

    ActivityThread thread = new ActivityThread();

    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }
    Looper.loop();

    ...
    
}

通過上面的源碼我們大概就能知道要想讓消息工作起來,主要有三個步驟,第一調(diào)用Looper.prepare,第二調(diào)用new Handler(),最后調(diào)用Looper.loop()讓整個消息機制工作起來。

看一下Looper.prepare()的源碼:

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

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));
}

代碼比較短,這里涉及到ThreadLocal這個類,簡單說一下這個類,ThreadLocal中保存的變量,作用域只在當前線程中,我們看一下異常的說明就知道了Only one Looper may be created per thread:每個線程只能有一個 Looper。

接下來看一下Handler的構造函數(shù):

public Handler() {
    this(null, false);
}

public Handler(Callback callback, boolean async) {
    
    ...

    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 = callback;
    mAsynchronous = async;
}

我們看到有個mLooper變量會讀取當前線程中的Looper,如果為空,也會拋出異常。
從上面兩部分的源碼,就能想到,為什么平時我們在UI線程中直接執(zhí)行new Handler()是可以的,而在子線程中執(zhí)行會報錯,因為在應用啟動時,主線程中的Looper已經(jīng)初始化好了。

最后一步就是Looper.loop()函數(shù)了,這步的工作就是不斷從MessageQueue中取消息,取不到就阻塞,取到就分發(fā)給Handler,這部分代碼的細節(jié),同學們自己去挖掘。

二、主線程的Handler分析

來看一下在ActivityThread中初始化的Handler:

private class H extends Handler {
    public static final int LAUNCH_ACTIVITY         = 100;
    public static final int PAUSE_ACTIVITY          = 101;
    public static final int PAUSE_ACTIVITY_FINISHING= 102;
    public static final int STOP_ACTIVITY_SHOW      = 103;
    public static final int STOP_ACTIVITY_HIDE      = 104;
    public static final int SHOW_WINDOW             = 105;
    public static final int HIDE_WINDOW             = 106;
    public static final int RESUME_ACTIVITY         = 107;
    public static final int SEND_RESULT             = 108;
    public static final int DESTROY_ACTIVITY        = 109;
    public static final int BIND_APPLICATION        = 110;
    public static final int EXIT_APPLICATION        = 111;
    public static final int NEW_INTENT              = 112;
    public static final int RECEIVER                = 113;
    public static final int CREATE_SERVICE          = 114;
    public static final int SERVICE_ARGS            = 115;
    public static final int STOP_SERVICE            = 116;
    
    ...

    public void handleMessage(Message msg) {
        if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
        switch (msg.what) {
            case LAUNCH_ACTIVITY: {
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
                final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
    
                r.packageInfo = getPackageInfoNoCheck(
                        r.activityInfo.applicationInfo, r.compatInfo);
                handleLaunchActivity(r, null);
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            } break;
            case RELAUNCH_ACTIVITY: {
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");
                ActivityClientRecord r = (ActivityClientRecord)msg.obj;
                handleRelaunchActivity(r);
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            } break;
            
            ...
            
        }
    
    }
}

這部分代碼包含了很多的常量定義,從命名上我們可以感覺出來 ,好像都是和生命周期相關的,下面是一個handlerMessage來處理消息,由此我們就知道了為什么在ActivityService、BroadCast這些組件的生命周期函數(shù)中,不能做耗時的任務,因為全是在主線程上運行的,耗時任務會導致ANR。之所以在這些函數(shù)中沒法執(zhí)行耗時任務,就誕生了一系列的工具類:IntentService、HandlerThread、LocalBroadcastManager。簡單的看下源碼你就會發(fā)現(xiàn):

1、HandlerThread是一個自帶Looper的線程,通過這個Looper可以創(chuàng)建Handler來實現(xiàn)子線程與主線程收發(fā)消息;
2、IntentService內(nèi)部實現(xiàn)了HandlerThread來執(zhí)行耗時任務,執(zhí)行完成自動銷毀Service;
3、LocalBroadcastManager被稱為本地廣播,之所以這么稱呼,因為在其內(nèi)部有主線程的Looper,所以可以給主線程發(fā)送任意消息,這個工具通常用作應用內(nèi)的通信。

通過源碼我們可以看到,消息機制在 Android 系統(tǒng)中的無處不在,同學們挖掘到更多使用Handler的地方,歡迎留言討論。

延伸閱讀:
1、理解Java中的ThreadLocal
2、詳解 Android 中的 HandlerThread
3、Android IntentService完全解析 當Service遇到Handler
4、LocalBroadcastManager 的實現(xiàn)原理

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

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

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