1. 程式人生 > >【Android自助餐】Handler訊息機制完全解析(四)Looper解析

【Android自助餐】Handler訊息機制完全解析(四)Looper解析

Android自助餐Handler訊息機制完全解析(四)Looper解析

Looper

如果你搞過Arduino,那麼你肯定知道這個loop()方法。沒接觸過也沒關係,這個方法就是一個通過死迴圈來重複做某件事的方法。區別是Arduion的迴圈控制在loop()

方法外,而Looper的迴圈控制在loop()方法內。這個Looper類本身則是對這個方法做了一些封裝。

初始化prepare()

這裡標題不是構造方法,因為其構造方法被private修飾,那麼來看看它什麼時候呼叫了構造方法。很容易就能找到private static void prepare(boolean quitAllowed)方法,該類僅在這裡呼叫了構造方法,然而這個prepare()也是被private修飾的,那麼來看看這個帶參的prepare()又在哪裡被呼叫了。結果可以找到兩個public static修飾的方法:prepare()prepareMainLooper()


第一個方法原文說明如下:

Initialize the current thread as a looper.This gives you a chance to create handlers that then reference this looper, before actually starting the loop. Be sure to call loop() after calling this method, and end it by calling quit().

某以不才為諸君翻譯如下:

作為looper初始化當前執行緒。提供一個機會來建立handler並使用looper。在使用之前,請在此方法之後呼叫loop()

,並在結束時呼叫quit()

第二個方法原文說明如下:

Initialize the current thread as a looper, marking it as an application’s main looper. The main looper for your application is created by the Android environment, so you should never need to call this function yourself.

某再以不才為諸君奉上槽點:

作為looper初始化當前執行緒,並標記其為application主執行緒的looper。在application主執行緒中的looper被Android系統建立,因此開發者請永遠不要手動呼叫這個方法。

不讓掉還不讓研究麼,今天我們就看這個prepareMainLooper ()幹了點啥。先把原始碼放上來:

public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

首先呼叫了這個私有的帶參的prepare()方法並傳了個false。這個私有方法宣告為private static void prepare(boolean quitAllowed),因此判斷該引數的含義是不允許退出。進來這個帶參方法看看:

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

這裡出現了sThreadLocal欄位,型別是ThreadLocal,先來看看該類的原文說明:

Implements a thread-local storage, that is, a variable for which each thread has its own value. All threads share the same ThreadLocal object, but each sees a different value when accessing it, and changes made by one thread do not affect the other threads. The implementation supports null values.

某以不才:

實現一個執行緒本地儲存,是什麼呢?一個讓每個執行緒都有擁有value的變數。所有的執行緒都共享同一個ThreadLocal物件,但每個執行緒在這拿到的value都不相同,並且一個執行緒在這裡做的改變並不影響其他執行緒。支援值為null

如果理解不了可以類比一下View類,該類有個setTag()方法,用來讓這個View攜帶附加值。此處的ThreadLocal就是讓執行緒可以攜帶附加值,因此也有get()set()方法。兩個方法中的第一句便是Thread.currentThread()來獲取當前執行緒,所以能看出上面那句“共享同一個物件”且“各執行緒互不影響”。

回到Lopper的帶參prepare(),可以看到new了一個Lopper並放到當前執行緒的“tag”中,而此處是從prepareMainLopper()進來的,因此當前執行緒便是主執行緒。
再看構造方法,裡面只有兩行程式碼:

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

建立了訊息佇列,並獲取了當前執行緒。上文提到在Handler構造方法中有mQueue = looper.mQueue;,說明Handler中的佇列與Lopper中的佇列是同一個佇列。
到這裡prepareMainLooper()就執行完了,根據空參的prepare()方法說明推斷,“Android Environment”在呼叫prepareMainLooper()後必然會呼叫loo()。檢視prepareMainLooper()呼叫者可以看到,在SystemServer.run()ActivityThread.main()中都在呼叫Looper.prepareMainLooper()後不遠就呼叫了Looper.loop()。而這兩處可以推斷一個是系統應用的主執行緒,一個是使用者應用的主執行緒。

提供looper獲取介面myLooper()

prepare()中提到了會將looper放到執行緒儲存ThreadLocal中,此處只需要從中取出並返回即可,因此程式碼只有一行return sThreadLocal.get();

處理訊息佇列loop()

先上核心原始碼:(有刪減)

for (;;) {
    Message msg = queue.next(); 
    if (msg == null) {
        return;
    }
    msg.target.dispatchMessage(msg);
    msg.recycleUnchecked();
}

這裡就一目瞭然了:一個死迴圈,不斷從佇列中取訊息並分發,如果取到null就說明訊息佇列已經退出或被釋放,此時loop終止。關於queue.next()可以看MessageQueue的佇列管理瞭解。msg.recycleUnchecked()可以看Message中obtain()與recycle()的來龍去脈瞭解。msg.target.dispatchMessage(msg)中target便是在Handler.obtainMessage()時放到訊息中的handler,dispatchMessage()便是對訊息的處理了。