從源分析Handler、MessageQueue、Looper

前言

很長的一段時間我一直在使用Handler,主要是在處理異步任務的時候來實現線程切換,不過對其原理和工做流程並無詳細的作過了解,如下我把從分析源碼獲得一些內容作出了一些總結。java

從源分析Handler/MessageQueue/Looper的工做流程

首先來看下以下的示意圖,圖中描述的對象之間的基本通訊。異步

首先是Handler對象發送了一條Message,而後消息會被存放到一個列表(隊列:MessageQueue)中,緊接着有一個叫作Looper的對象會不停的去這個隊列中尋找有沒有新的消息,有的話將消息分配給Handler對象進行處理(每個Message對象都會默認持有一個Handler對象的引用,這個Handler對象就是發送這個Message的對象,在Message對象內部被定義爲target變量)。其具體的消息會被放在target所在的線程中執行。接下來詳細介紹消息的收發和處理過程。async

https://imgservice.lost520.cn/InternetImgService/2019-07-24/20190724222357343.pn

Handler的建立過程

首先先來看一下Handler的構造函數,以下圖,Handler一共向外提供了4個構造函數(其實在內部一共提供了是7個構造函數,只不過對外是隱藏的)。ide

Handler的構造函數

//Step1
public Handler() {
    this(null, false);
}
//Step2
public Handler(Callback callback, boolean async) {
    //.........此處省略了部分代碼

    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread " + Thread.currentThread()
                    + " that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}
複製代碼

能夠看到的是咱們在調用無參數構造方法時其實的調用的內部的具備兩個參數的構造方法,第一個要求傳入一個回調實現(後便會具體介紹),第二個則是否異步的標識符。函數

Step2中能夠看到幾個比較關鍵的內容,第一個關鍵點就是在咱們新建的Handler對象內部保存了一個Looper對象的引用,這個Looper.myLooper()函數獲取的是當前線程所持有的Looper對象(線程中默認是沒有Looper對象的,只有調用Looper.propare()函數以後纔會在當前線程中建立一個惟一的Looper對象,因此若是沒有則會拋出一個異常,這個異常就是咱們最初在子線程中使用Handler提示的異常信息。);第二個關鍵點則是從Looper對象中拿到了一個消息隊列對象mQueue,這個對象是一個MessageQueue,它是在Looper被建立時建立的。oop

Looper/MessageQueue的建立過程/時機

MessageQueue是跟隨Looper的建立而建立的,在一個線程中只會存在一個Looper對象,也就是說在一個線程中MessageQueue也只會存在一個(理論上來講)。下面從源碼中來印證如上所說。ui

一、來看Looper.prepare()函數this

這個方法僅僅是提供了一個入口方法,實際上調用的是內部的另外一個prepare方法。緊接着內部的這個prepare方法經過new的方式建立了一個Looper對象,也就是在Step3的內容。能夠清楚的看到這裏爲Looper的內部變量mQueue進行了賦值,也就是在這個時候MessageQueue被建立。spa

在Step2的時候咱們發現調用了一個叫作sThreadLocal的變量的set函數,這個ThreadLocal並不是是一個線程,它是用來存儲線程中數據的,具體可參考個人另外一篇文章:ThreadLocal是什麼?線程

//Step1
public static void prepare() {
    prepare(true);
}
//Step2
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));
}
//Step3
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}
複製代碼

在Looper對象中有一個很是重要的函數,那就是loop了,中文翻譯過來就是循環的意思,這個函數會幫助咱們不停的從MessageQueue中來獲取Message

二、來看MessageQueue

目前來講,MessageQueue的建立並無什麼值得咱們關注的,它只是提供了一個先進先出的機制來幫助咱們存取消息,可是咱們須要知道它所提供的兩個很是重要的方法,第一個就是enqueueMessage,這個函數是用於將Message對象添加到隊列中的,第二個就是next函數,該函數是從消息隊列中取消息的,取出來後會馬上從隊列中移除。

Handler的消息發送過程

以下是一個基本的消息發送流程,基本使用這裏不在贅述。

Handler handler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        //do something...
    }
};
Message message = Message.obtain();
handler.sendMessage(message);
複製代碼

一、走進handler.sendMessage(Message msg)函數中來一探究竟。

//Step1
public final boolean sendMessage(Message msg) {
    return sendMessageDelayed(msg, 0);
}
//Step2
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
//Step3
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
    }
    return enqueueMessage(queue, msg, uptimeMillis);
}
複製代碼

如上能夠看到的是在咱們調用handler.sendMessage(Message msg)函數時,它會調用內部的sendMessageDelayed函數,這個函數是用於發送定時消息的,由於sendMessage發送的都是須要當即被處理的消息,因此傳入的就是0了,緊接着sendMessageDelayed函數又調用了sendMessageAtTime函數。

在這個sendMessageAtTime函數中咱們須要關注的是enqueueMessage的調用,這個enqueueMessage函數是幫助咱們把消息加入到MessageQueue對象中。在如上Hanlder建立過程的描述中,咱們說了:這個消息隊列(mQueue對象)是在Handler建立時從Looper對象中獲取並保存到局部變量中的。

二、來看Handler中的enqueueMessage函數

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}
複製代碼

這裏有兩點咱們須要特別關注,第一個就是把當前Handler對象的引用給了msgtarget變量,這其實就是爲以後的消息處理提供處理者,在加入到消息隊列以前會默認把Messagetarget設置爲發送MessageHandler對象,即使咱們在建立Message對象時設置了target也會在enqueueMessage函數中被重置,由此能夠得出,Message對象的發送者便是Message的處理者。

到這一步消息已經被添加到了MessageQueue對象中,至此HandlersendMessage任務就算完成了,也就是說它成功的將消息遞交給了MessageQueue對象。

Message的處理過程

在上一段的結尾咱們知道了HandlersendMessage函數會把咱們的Message對象加入到一個叫作MessageQueue的對象中,也就是說咱們只是把消息保存了起來,單純的消息保存沒有任何意義,因此引入了Looper對象來不停的從MessageQueue中拿數據而且交給消息的target對象來進行處理。

一、來看Looperloop函數

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

    //.......此處省略無關代碼

    boolean slowDeliveryDetected = false;

    for (;;) {
        //核心3
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
        //.......此處省略無關代碼
        try {
            msg.target.dispatchMessage(msg);//核心4(重點!!!!!!)
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        //.......此處省略無關代碼
    }
}
//核心1調用的myLooper函數
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}
複製代碼

以上就是Looperloop函數了,爲了便於觀看,這裏我刪減掉了無關的代碼,並標註了4條較爲重要的代碼。

第一步:調用Looper對象內部的myLooper()函數,這個函數是從ThreadLocal對象中取出當前所在線程的Looper對象,它是咱們在建立Looper時保存的Looper對象,也就是咱們在上邊介紹Looper建立時看到的sThreadLocal.set(new Looper(quitAllowed));

第二步:拿到咱們當前線程中持有的MessageQueue對象,在上邊咱們說了MessageQueue是隨着Looper的建立而被建立的。也就是說咱們拿到的LooperMessageQueue都是當前線程中的。至此你應該要知道LooperMessageQueue在每個線程中都是能夠存在的,可是更要知道的是:在每個線程中有且只有一個LooperMessageQueue對象。以下我繪製了一張圖幫助更好記憶和理解。

https://user-gold-cdn.xitu.io/2019/7/25/16c2902e2ad7575b?w=966&h=663&f=png&s=22741

第三步:從MessageQueue中取出咱們使用Handler.sendMessage存放進取的消息。

第四部:這一步實際上是最核心的一部了,它經過調用Message對象中的target變量的dispatchMessage函數,將消息交給target對象進行處理,在上邊咱們說了target對象就是發送Message對象的Handler。因此最終的消息處理會被放在該對象中被處理。

二、來看HandlerdispatchMessage函數

以下就是咱們在上一步看到的loop函數中調用的dispatchMessage函數。

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}
 /** * Subclasses must implement this to receive messages. * 譯:子類必須實現這個函數來接收消息 */
public void handleMessage(Message msg) {
}
複製代碼

首先它會判斷Messagecallback變量是否爲NULL(在Message.obtain()函數中能夠傳入callback),若是存在callback那麼會優先處理Messagecallback,不然會繼續判斷當前Handler對象的callback是否爲NULL(這個callback是在構造Handler對象時是可選傳入的),若是還不行那麼就調用Handler中的handleMessage函數,這也是咱們常見的消息處理方式,也就是咱們在上邊重寫的handleMessage函數。

這裏咱們須要知道的就是消息處理的優先級:

一、由Message對象的callback處理(這個callback是一個Runnable對象)

二、由Handler對象的mCallback處理(這個callbackHandler對象中的一個接口提供了一個用於消息處理的回調public boolean handleMessage(Message msg);

三、由HandlerhandleMessage處理(這個就是Handler對象中的一個方法了,只有默認實現沒有任何代碼,一般須要重寫)

另外須要知道的是:dispatchMessage函數中全部的消息都是在Handler對象所處的線程中被執行的。

消息的發送和處理總結

一、調用Looper.prepare函數

幫助咱們建立Looper對象和MessageQueue對象。

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對象並保存當ThreadLocal中
}
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}
複製代碼

二、建立Handler對象

在這一步主要是拿到當前線程的Looper對象以及Looper對象中的MessageQueue對象並保存其引用。

public Handler(Callback callback, boolean async) {
        //........省略不重要代碼
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
複製代碼

三、調用HandlersendMessage函數

該函數最終會走到Handler對象中的enqueueMessage中,將消息保存到當前線程的MessageQueue對象中。

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}
複製代碼

三、Looper對象的loop函數

當前線程的Looper對象不斷的從它內部的MessageQueue對象中取消息,而後交給Messagetarget來作處理。

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

    //.......此處省略無關代碼

    boolean slowDeliveryDetected = false;

    for (;;) {
        //核心3
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
        //.......此處省略無關代碼
        try {
            msg.target.dispatchMessage(msg);//核心4(重點!!!!!!)
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        //.......此處省略無關代碼
    }
}
//核心1調用的myLooper函數
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}
複製代碼

四、Handler處理消息

到這裏會根據優先級來處理消息,且消息的執行是在當前Handler所在的線程中。

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

至此核心的處理流程及已經完成了。

主線程中不用手動建立Looper的緣由

Android主線程即ActivityThread,在主線程的入口方法main方法中調用了Looper的prepareMainLooper函數,該函數是專門爲主線程提供建立Looper使用的。

public static void main(String[] args) {
    //......省略無用代碼
    Looper.prepareMainLooper();

    // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
    // It will be in the format "seq=114"
    long startSeq = 0;
    if (args != null) {
        for (int i = args.length - 1; i >= 0; --i) {
            if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
                startSeq = Long.parseLong(
                        args[i].substring(PROC_START_SEQ_IDENT.length()));
            }
        }
    }
    ActivityThread thread = new ActivityThread();
    thread.attach(false, startSeq);

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

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

    // End of event ActivityThreadMain.
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    Looper.loop();

    throw new RuntimeException("Main thread loop unexpectedly exited");
}
複製代碼

總結

持有關係:

一、一個線程中只有一個Looper對象和一個MessageQueue 二、一個線程中能夠有多個Handler對象 三、MessageQueue是包含在Looper中的

注意點:

一、Handler必須在持有Looper的線程中才能建立。 二、Handler的回調優先級(一、Message.callback二、Handler.callback、三、Handler.handleMessage)。 三、在使用Handler發送Message時,Message的target會被默認設置爲Message的發送者。

最後

HandlerMessageMessageQueueLooper組成了Android強大的消息機制,以上只是簡述了其中的部份內容,還有不少的知識點等待往後進行挖掘。

原創文章,轉載請標明來源。

相關文章
相關標籤/搜索