Android消息機制系列(1)——Handler Looper Message源碼解析

一、基本概念:

在談到Android消息機制的時候,我們不可避免的要涉及要一下幾個概念:
1、Message 消息
2、MessageQueue 消息隊列
3、Handler 處理者?它的實際作用包括發送消息(sendMessage),處理消息(handleMessage)
4、Looper 消息循環,或者輪詢器

從字面上來看,前兩者都比較好理解。剩下的Handler、Looper,讓我們先重點理解一下Looper:

二、Looper

Looper類的作用:
Class used to run a message loop for a thread. Threads by default do not have a message loop associated with them; to create one, call prepare() in the thread that is to run the loop, and then loop() to have it process messages until the loop is stopped.

簡單翻譯過來就是:Looper用于管理一個線程的消息循環。 默認情況下,線程是沒有相關聯的Looper的,需要調用Looper.prepare()來創建,創建完成之后,Looper.loop()會不斷地循環來處理消息,直到循環停止。一般情況下是和Handler來配合使用的。

使用Looper+Handler典型代碼例如:

class LooperThread extends Thread {
    public Handler mHandler;
    public void run() {
        Looper.prepare(); //為本線程創建一個Looper
        mHandler = new Handler() {
            public void handleMessage(Message msg) {
            // process incoming messages here
            }
        };
        Looper.loop(); //消息循環
    }
}

Looper重要屬性:

// sThreadLocal.get() will return null unless you've called prepare()
static final ThreadLocal sThreadLocal = new ThreadLocal();
private static Looper sMainLooper;  // guarded by Looper.class
final MessageQueue mQueue;
final Thread mThread;

其中,sThreadLocal是一個ThreadLocal對象,可以在一個線程中存儲變量。

Looper的prepare()方法:

public static void prepare() {
    prepare(true);
}
private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) { //一個線程只能對應一個Looper實例
    throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed)); //new一個Looper的實例保存在ThreadLocal中
}

可以看到,prepare()方法將new一個Looper的實例,同時將該Looper實例放入了ThreadLocal。并且當第二次調用時報錯,說明prepare()方法不能被調用兩次,同時也保證了一個線程中只有一個Looper實例。

Looper的構造函數:

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

Looper構造函數中創建一個消息隊列MessageQueue。同時也可以看到,一個Looper實例對應一個線程、一個MessageQueue

Looper的loop()方法:

public static void loop() {
    // myLooper() 返回的是sThreadLocal.get(),也就是sThreadLocal存儲的Looper實例
    final Looper me = myLooper(); 
    if (me == null) {
        // 可以看到這里,如果在線程中沒有調用Looper.prepare()的話Looper實例是空的
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on    this thread.");
    }
    final MessageQueue queue = me.mQueue; //拿到Looper實例對應的消息隊列
    // 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;
    }
    ... ...
    msg.target.dispatchMessage(msg); //真正處理消息的地方
    ... ...
    // 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.recycleUnchecked();
    }
}

可以看到,方法中無限循環體里面,不斷地通過queue.next()來取出準備處理的消息。而真正處理消息的地方是在這一句:msg.target.dispatchMessage(msg)。target是什么?我們可以從Message源碼中可以看到,target是一個Handler,也就是說,真正處理消息是該消息對應的Handler實例的dispatchMessage(msg)方法中。

小結:
1、Looper用于管理一個線程的消息循環。
2、由于默認情況下,線程是沒有相關聯的Looper的,因此必須調用Looper.prepare()來創建。
3、Looper.prepare()方法,會使得線程綁定唯一一個Looper實例
4、Looper創建時也會創建一個消息隊列MessageQueue。
2、Looper.loop()方法,不斷從MessageQueue.next()中去取出準備處理的消息,交給消息的對應的handler的dispatchMessage去處理。

簡言之,Looper提供了存儲消息的隊列(MessageQueue),同時不斷循環取出準備處理的消息。那么誰來發送消息和真正處理消息呢?——當然是我們熟悉的Handler了。

三、Handler:

從第一部分我們可以推測出,Handler的兩個重要作用:
1、發消息到MessageQueue中
2、接受Looper分發的消息處理任務,真正處理消息

那么Handler是怎么和一個線程的MessageQueue、Looper實例關聯上的呢?

這個要看一下Handler是怎樣被new出來的。具體我會另開文分析,這里以開篇的例子來分析,當使用new Handler()來創建一個Handler的情況(意思是還有其他情況):

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

最后是調用了這個:

  public Handler(Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) {
        final Class 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();  //這里,關聯上了當前線程的Looper實例
    if (mLooper == null) {
        throw new RuntimeException( 
        // 在一個線程里面,創建handler之前必須調用Looper.prepare()
        "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue; // 這里,取得當前線程的Looper實例的消息隊列
    mCallback = callback;
    mAsynchronous = async;
}

可以看到,在這種情況下,在構造函數里面,通過Looper.myLooper()獲取了當前線程保存的Looper實例,然后又獲取了這個Looper實例中保存的消息隊列MessageQueue。Handler實例就是這樣和一個線程的MessageQueue、Looper實例一一關聯上的。

這里的當前線程,是指創建Handler實例所使用的線程。

接下來就簡單了,用Handler發送一條消息,不論使用何種方法,最終都會調到這個函數:

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue; //當前線程對應Looper實例的消息隊列
    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);
}

然后調用了這個函數:

private boolean enqueueMessage(MessageQueue queue, Message msg,long     uptimeMillis) {
    msg.target =this; //將消息的target設為當前的Handler實例
    if(mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis); //將消息插入到消息隊列中去
}

重要的是這句:msg.target = this; //將消息的target設為當前的Handler實例

還記得第一部分結尾處,loop()方法中取出一條要處理的消息,然后調用msg.target.dispatchMessage(msg)嘛?那么現在我們知道了,msg的target就是在發這條消息的時候設置上的。以保證后續處理消息的時候,能找到處理這條消息的Handler。

queue.enqueueMessage(msg, uptimeMillis); //這句將設置好target等屬性的消息放到消息隊列中去

終于,讓我們看一下msg.target.dispatchMessage(msg)的dispatchMessage()函數:

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

public void handleMessage(Message msg) {
}

這個方法最終調用我們在創建Handler時重寫的handleMessage()方法或者callback來進行消息處理;

而handleMessage()我們就很熟悉了,每個創建Handler的都要重寫handleMessage()方法,根據msg.what來處理消息,例如:

private Handler mHandler = newHandler() {
    public void handleMessage(android.os.Message msg) {
        switch(msg.what) {
            case:
            break;
            default:
            break;
        }
     };
};

三、總結

1、默認情況下線程是沒有Looper實例的,Looper.prepare()為線程關聯到唯一的Looper實例,以及Looper實例的MessageQueue。

2、Handler被創建時,會關聯到“某個線程”的唯一的Looper實例和MessageQueue;Handler的主要作用是將處理消息切換到“這個線程”來處理。

3、當通過Handler發送一條Message時,該消息的target就被設為這個Handler實例(this)

4、Handler發送一條消息,實際上是將這條消息插入到對應的消息隊列MessageQueue中

5、線程對應的Looper實例loop()方法來處理消息時,會根據這條消息的target(也就是Handler實例),回調Handler的dispatchMessage()方法進行處理。dispatchMessage()方法最終調用我們在創建Handler時重寫的handleMessage()方法或者callback。

這里面,比較重要的是,明確Handler被創建時,和哪個線程或者和哪個線程的Looper相關聯,這將決定了任務最后在哪個線程執行;

這里面有個特殊情況,就是在主線程創建Handler時不需要調用prepare()和loop(),因為這部分工作Android已經幫我們做過了,具體可以看一下ActivityThread的代碼。

簡單的說,一條消息從發出到處理經歷了:

[Handler發送消息] -> [消息進入Looper的MessageQueue隊列] -> [loop循環從MessageQueue取出要處理的消息] -> [Handler處理消息]


Reference:

Handler源代碼

Looper源代碼

Android 異步消息處理機制 讓你深入理解 Looper、Handler、Message三者關系

Android異步消息處理機制完全解析,帶你從源碼的角度徹底理解

Android的消息機制之ThreadLocal的工作原理

Android中為什么主線程不會因為Looper.loop()里的死循環卡死?

Android異步消息處理機制詳解及源碼分析

Handler常見用法

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容