1. 程式人生 > >Handler機制及原理探究

Handler機制及原理探究

Handler使用簡單功能強大,常被用作執行緒間傳遞訊息的元件,而且還可以用於跨程序。

訊息機制背後有包括Looper ,MessageQueue管理和分發訊息的實現,同時在Native層也單獨實現了一套類似的機制,接收和處理Native層的訊息。Java層和Native層的訊息迴圈是獨立執行的,彼此的Message並不會互通,Native使用epoll機制來實現監聽及觸發,並向JAVA層提供了介面。

這裡從Java層開始深入探究下Handler和訊息機制背後實現的原理。


初探Handler

本章只作為入口,先從巨集觀上去了解其中的架構,之後再做深入分析。

程式碼架構

Handler本身只負責傳送和處理接收到的訊息,其背後有一個訊息迴圈為它管理和提供訊息。 
MessageQueue是管理著Message連結串列;而Looper是訊息迴圈的主體,負責迴圈從MessageQueue中獲取需要處理的新訊息並向Handler輸送。 
這裡寫圖片描述

其中MessageQueue有部分核心實現在native層(後續會講到)。

MessageQueue.java
   private native static long nativeInit();
   private native static void nativeDestroy(long ptr);
   private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/
   private native static void nativeWake(long ptr);
   private native static boolean nativeIsPolling(long ptr);
   private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

native方法實現在JNI層的android_os_MessageQueue.cpp:

android_os_MessageQueue.cpp
static const JNINativeMethod gMessageQueueMethods[] = {
    /* name, signature, funcPtr */
    { "nativeInit", "()J", (void*)android_os_MessageQueue_nativeInit },
    { "nativeDestroy", "(J)V", (void*)android_os_MessageQueue_nativeDestroy },
    { "nativePollOnce", "(JI)V", (void*)android_os_MessageQueue_nativePollOnce },
    { "nativeWake", "(J)V", (void*)android_os_MessageQueue_nativeWake },
    { "nativeIsPolling", "(J)Z", (void*)android_os_MessageQueue_nativeIsPolling },
    { "nativeSetFileDescriptorEvents", "(JII)V",
            (void*)android_os_MessageQueue_nativeSetFileDescriptorEvents },
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

什麼是Handler?

開啟Handler.java,先看下googler留給開發者留的一段說明:

/**
 * A Handler allows you to send and process {@link Message} and Runnable
 * objects associated with a thread's {@link MessageQueue}.  Each Handler
 * instance is associated with a single thread and that thread's message
 * queue.  When you create a new Handler, it is bound to the thread /
 * message queue of the thread that is creating it -- from that point on,
 * it will deliver messages and runnables to that message queue and execute
 * them as they come out of the message queue.
 * 
 * <p>There are two main uses for a Handler: (1) to schedule messages and
 * runnables to be executed as some point in the future; and (2) to enqueue
 * an action to be performed on a different thread than your own.
 * 
 * <p>Scheduling messages is accomplished with the
 * {@link #post}, {@link #postAtTime(Runnable, long)},
 * {@link #postDelayed}, {@link #sendEmptyMessage},
 * {@link #sendMessage}, {@link #sendMessageAtTime}, and
 * {@link #sendMessageDelayed} methods.  The <em>post</em> versions allow
 * you to enqueue Runnable objects to be called by the message queue when
 * they are received; the <em>sendMessage</em> versions allow you to enqueue
 * a {@link Message} object containing a bundle of data that will be
 * processed by the Handler's {@link #handleMessage} method (requiring that
 * you implement a subclass of Handler).
 * 
 * <p>When posting or sending to a Handler, you can either
 * allow the item to be processed as soon as the message queue is ready
 * to do so, or specify a delay before it gets processed or absolute time for
 * it to be processed.  The latter two allow you to implement timeouts,
 * ticks, and other timing-based behavior.
 * 
 * <p>When a
 * process is created for your application, its main thread is dedicated to
 * running a message queue that takes care of managing the top-level
 * application objects (activities, broadcast receivers, etc) and any windows
 * they create.  You can create your own threads, and communicate back with
 * the main application thread through a Handler.  This is done by calling
 * the same <em>post</em> or <em>sendMessage</em> methods as before, but from
 * your new thread.  The given Runnable or Message will then be scheduled
 * in the Handler's message queue and processed when appropriate.
 */
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

既然決定來了就不能放過每個細節,記錄下重點。 
運作方式: 
每一個Handler例項只與一個單獨的Thread和這個Thread的MessageQueue關聯; 
當我們在Thread中建立一個新的Handler時,會繫結這個Thread和Thread的MessageQueue,之後Handler允許我們向MessageQueue傳送Message和Runnable,並在訊息出列時處理它們。

Handler的2個主要用途: 
1. 讓Message和Runnable可以延遲執行; 
2. 在另外一個執行緒中執行處理。

用法: 
通過Post開頭和sendMessage開頭的方法可以傳送訊息到MessageQueue。 
1. post開頭的方法可以向佇列插入Runnable; 
2. sendMessage開頭的方法則用於來送Message,Message將在handleMessage方法中被處理。 
3. post和send方法既可以讓訊息“實時”被處理(相對於延時),也可以設定特定的時延,延時去處理。

建議: 
應用程序中的的主執行緒是專門用於管理頂層的資料的,例如activity/廣播/視窗等,不宜處理其他我們定義的耗時操作,因此我們應該建立自己的工作執行緒,通過Handler來向執行緒的MessageQueue傳送要執行的任務。

三個需要理解的問題

看完上面這段話,有3個疑問需要探究: 
1. Handler如何與Thread關聯? 
2. Thread和MessageQueue的關係是? 
3. MessageQueue如何運作?它如何管理Runnable和Message?

後面對這些問題一一破解。


從如何使用Handler開始

怎麼樣才能使Handler正常運作?

例子1——定義在子執行緒的Handler

public class MainActivity extends AppCompatActivity {

    Handler mHandler;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Log.i("handler.demo", "Main Thread:" + Thread.currentThread().toString());
        new MyThread().start();
        //確保Handler已經在子執行緒中例項化
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                Log.i("handler.demo", "Running in Thread:" + Thread.currentThread().toString());
            }
        });
    }

    class MyThread extends  Thread{

        @Override
        public void run() {
            Looper.prepare();
            mHandler = new Handler();
            Looper.loop();
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

Log輸出,runnable的run被呼叫,而且執行在子執行緒Thread-4中:

06-10 16:48:51.077 17181-17181/? I/handler.demo: Main Thread:Thread[main,5,main]
06-10 16:48:52.078 17181-17199/com.example.willis.myapplication I/handler.demo: Running in Thread:Thread[Thread-4,5,main]
  • 1
  • 2

例子2——定義在主執行緒中的Handler

將上面的例子稍作修改,就可以改成子執行緒向主執行緒Handler傳送訊息:

public class MainActivity extends AppCompatActivity {

    Handler mHandler = null;
    Object mLock = new Object();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.i("handler.demo", "Main Thread:" + Thread.currentThread().toString());
        mHandler = new Handler();
        new MyThread().start();
    }

    class MyThread extends  Thread{
        @Override
        public void run() {
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    Log.i("handler.demo", "Running in Thread:" + Thread.currentThread().toString());
                }
            });
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

Log輸出看到Runnable.run()就在主執行緒執行:

06-10 17:00:24.073 17526-17526/com.example.willis.myapplication I/handler.demo: Main Thread:Thread[main,5,main]
06-10 17:00:24.095 17526-17526/com.example.willis.myapplication I/handler.demo: Running in Thread:Thread[main,5,main]
  • 1
  • 2

例子3——跨程序傳遞訊息

除了線上程間使用外,Handler還可以通過IMessenger和Message來實現程序間的訊息傳遞。 
因為Message本身實現了Parcelable介面支援跨程序,Handler中定義了繼承IMessenger.Stub的MessengerImpl類作為跨程序傳入Message的入口。程序外通過Handler.getIMessenger()方法獲得此Handler的IMessenger即可向它傳送訊息。

Handler.java
IMessenger mMessenger;

final IMessenger getIMessenger() {
    synchronized (mQueue) {
        if (mMessenger != null) {
            return mMessenger;
    }
    mMessenger = new MessengerImpl();
    return mMessenger;
   }
}

private final class MessengerImpl extends IMessenger.Stub {
    public void send(Message msg) {
        msg.sendingUid = Binder.getCallingUid();
        Handler.this.sendMessage(msg);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

Android framework中實現了一個叫AsyncChannel的類,利用Handler誇程序特性,支援2個程序間的Handler互通訊息,有興趣可以看一下: 
/frameworks/base/core/java/com/android/internal/util/AsyncChannel.java

第四個問題

例子1中,Looper.prepare()Looper.loop()方法呼叫順序是固定的,必須在handler建立前執行Looper.prepare(),在Handler建立後執行Looper.loop(),這樣Handler才能正常執行線上程中。 
但例子2中並沒有看到Looper的身影,這是為什麼?

第四個問題: 
Looper是什麼?為什麼例子中2個Looper方法要按這樣的順序呼叫?


Handler和Looper的關係

通過第一個例子可以猜測,Looper應該就是維護訊息迴圈的地方,且Handler的構造方法中一定有某些東西關聯到Looper,於是先從Handler的構造方法入手。

Handler構造方法

Handler有6個有參構造方法,另外還有1個個無參構造方法: 
這裡寫圖片描述

這是其中一個Handler的有參構造方法,儲存了Looper,MessageQueue等例項,可以說明Handler是直接依賴於Looper的

Handler.java

final Looper mLooper;
final MessageQueue mQueue;
final Callback mCallback;
final boolean mAsynchronous;

public Handler(Looper looper, Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

而Handler無參構造方法內部呼叫了另一個有參構造方法,最後在該方法中還是通過Looper.myLooper()方法獲取到了Looper例項:

Handler.java
public Handler() {
    this(null, false);
}
public Handler(Callback callback, boolean async) {
    //實際上還是獲取到了Looper
    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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

Looper.myLooper()方法中呼叫ThreadLocal.get()返回一個Looper例項:

Looper.java
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}
  • 1
  • 2
  • 3
  • 4

既然能get到一個Looper,那麼繼續尋找sThreadLocal在何處去set這個Looper。 
結果找上了Looper.prepare()方法:

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

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

//唯一構造方法是private的,建立了MessageQueue,並儲存了當前的Thread例項
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);//建立MessageQueue,引數指定了這個queue是否能被退出
    mThread = Thread.currentThread();//本執行緒
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

Looper.prepare()內部新建了Looper例項,並set到sThreadLocal,那麼,就足以解釋為什麼在new Handler()之前必須呼叫Looper.prepare()

——因為Handler需要獲取到Looper例項,而Looper.prepare()就是建立Looper的地方。


那麼Looper.loop()為什麼要在最後執行呢?

Looper.java
public static void loop() {
        final Looper me = myLooper();
        final MessageQueue queue = me.mQueue;
        ...
        for (;;) {
            //獲取下一個訊息
            Message msg = queue.next(); // might block
            if (msg == null) { 
                //退出迴圈
                return;
            }

            try {
                //向Handler分發訊息
                msg.target.dispatchMessage(msg);
            } finally {
                ...
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            msg.recycleUnchecked();
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

明顯Looper.loop()方法內部是個死迴圈,迴圈從MessageQueue中獲取訊息並分發給Handler,因此loop()方法必須是最後執行的。


最後,來看為什麼第二個例子中,主執行緒建立Handler時不用顯式地初始化Looper? 
根據上面2個問題的答案思考下,Handler初始化是必須獲得Looper的,而Looper只有在Looper.prepare()方法中建立。順藤摸瓜,在Looper.java中找到了一個叫prepareMainLooper的方法,不僅建立了Looper物件,而且將它儲存到了sMainLooper變數中。

Looper.java

    private static Looper sMainLooper;  //主執行緒的Looper

    public static void prepareMainLooper() {
        prepare(false);//新建Looper,指定
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            //復值到sMainLooper
            sMainLooper = myLooper();
        }
    }

    //quitAllowed引數指定了MessageQueue是否允許退出
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

搜尋下Looper.prepareMainLooper()的呼叫者,竟然是ActivityThread.main()方法。就是說,在Activity建立的時候,主執行緒建立了自己的main looper,並同樣地開啟了無限迴圈模式!這側面映證了Android應用執行是靠訊息驅動的。

ActivityThread.java
public static void main(String[] args) {
        ...
        Process.setArgV0("<pre-initialized>");
        //建立主執行緒Looper
        Looper.prepareMainLooper();
        ActivityThread thread = new ActivityThread();
        thread.attach(false);
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
        //主執行緒的Event loop開始迴圈
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

本章小結

至此,前面提出的4個大問題已經解決了3個:

1. Handler如何與Thread關聯? 
——這裡需要加入Looper的概念。 
Handler線上程中建立時獲取到looper例項,而Looper線上程中執行訊息迴圈,並分發給Handler。

另外:Looper的訊息迴圈是死迴圈,因此一個Thread中只能執行一個Looper。而Handler作為訊息的傳送和處理者,與Looper的關係是多對一的。 
因此它們3者的關係是: 1 Looper - 1 Thread - N Handler 
這裡寫圖片描述

2. Thread和MessageQueue的關係是? 
——MessageQueue在Thread對應的Looper中建立,用於儲存訊息,訊息最後會分發給Handler處理。

  1. MessageQueue如何運作?它如何管理Runnable和Message? 
    ——待下一章分析

4. Looper是什麼?為什麼例子中2個Looper方法要按固定的順序呼叫? 
分為3個小問題: 
當Handler被定義在子執行緒中時,為什麼在new Handler之前必須先Looper.prepare()? 
——因為Handler需要獲取到Looper例項,而Looper.prepare()就是建立Looper的地方。

為什麼Looper.loop()要最後執行? 
——因為內部實現了一個死迴圈,用作訊息的讀取和分發,在Looper.quit()被呼叫之前,loop迴圈會一直進行。

在主執行緒中實現的Handler,為什麼無需顯式地呼叫Looper的初始化方法? 
——Activity主執行緒啟動時,已經建立好了Looper,我們在Activity中新建的Handler預設繫結這個主執行緒Looper。

接下來深入分析流程。


訊息傳送及分發流程

接下來具體討論訊息如何插入,以及如何分發。

Message的組成和訊息池

首先來了解下Message是什麼。 
通過文章開頭的類圖可以看到,Message是一個數據類,包含使用者定義的資料,Runnable例項,關聯的Handler。 
同時,下一個Message的例項儲存在next變數中,可見Message將以鏈的形式儲存。

    //相當於訊息的ID,用於在處理時識別訊息
    public int what;
    //arg1和arg2用於儲存int型別的資料
    public int arg1; 
    public int arg2;

    //儲存Object型別的引數,如果在跨程序使用時只支援framework實現的可跨程序的物件
    public Object obj;

    //儲存Bundle形式的資料引數
    /*package*/ Bundle data;

    //Messenger形式的訊息接受者
    public Messenger replyTo;

    //關聯的Handler(訊息接收者)
    /*package*/ Handler target;

    //將被執行的runnable
    /*package*/ Runnable callback;

    //下一個訊息
    /*package*/ Message next;

    //訊息池,Message的重用管理,通過obtain()方法獲取可重用的訊息
    private static Message sPool;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

Message中還實現了訊息重用,如果使用Message.obtain()方法獲取Message,將返回可重用的Message。

    private static Message sPool;

    public static Message obtain() {
        synchronized (sPoolSync) {
            //如果pool中有可重用的message則直接返回
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

    //Looper.loop()處理完一個訊息後,會呼叫此方法去“回收”Message例項
    //實際上Message將被重置並放入“訊息池”中。
    void recycleUnchecked() {
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = -1;
        when = 0;
        target = null;
        callback = null;
        data = null;

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

Looper中的訊息迴圈和訊息分發

正如前文看到Looper.loop()開啟了一個死迴圈,從MessageQueue的next方法獲取訊息後,分發給Handler處理。這裡先快速地看下訊息如何分發,然後來重點看MessageQueue的next方法。

Looper.java
/**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    public static void loop() {
        final Looper me = myLooper();
        ...
        final MessageQueue queue = me.mQueue;
        ...

        for (;;) {
            //獲取下一個Message,沒有訊息時可能會阻塞
            Message msg = queue.next(); // might block
            ...
            try {
                //執行runnable,或者分發訊息給handleMessage()或callback
                msg.target.dispatchMessage(msg);
            } finally {

            }
            ...
            msg.recycleUnchecked();
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

迴圈從MessageQueue.next()中獲取訊息,這裡先來看訊息的分發:Handler.dispatchMessage(): 
1. 如果通過post(Runnable)傳送的Message,那麼只執行Runnable.run()。 
2. 如果如果實現了Handler.Callback介面,則訊息分發給Callback.handleMessage()方法處理,返回ture就不會執行第三步 
3. 由Handler.handleMessage()處理

    /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            //執行runnable
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                //分發給Handler.Callback處理
                //如果實現了Handler.Callback介面,則訊息可以在這裡被處理
                //如果執行完Callback.handleMessage後返回true,則不再分發給Handler.handleMessage()處理
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            //直接在handleMessage中處理
            handleMessage(msg);
        }
    }

    private static void handleCallback(Message message) {
        message.callback.run();
    }

    public interface Callback {
        public boolean handleMessage(Message msg);
    }

    /**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(Message msg) {
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

獲取訊息:MessageQueue.next()方法

MessageQueue的next方法也是一個迴圈,主要目的是獲取下一個要被處理的Message,其中的幾個要點: 
1. nativePollOnce是阻塞的,中間執行了epoll_wait等待,通過nativeWake主動喚醒或者到達超時時間後喚醒。 
2. 如果插入了SyncBarrier訊息(handler為null的訊息),則只會處理“非同步”的訊息(設定了Asynchronous flag的訊息,詳看後文) 
3. 如果當前訊息沒有到達when設定的時間,則會重新進入nativePollOnce,設定具體的超時時間 
4. 到達設定時間的Message會被返回,由Looper分發處理。 
5. 如果進入next()時沒有訊息要被馬上處理,則會執行IdleHandler的處理。

Message next() {
        final long ptr = mPtr;
        ...

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            ...
            //沒有訊息需要被處理時會阻塞
            nativePollOnce(ptr, nextPollTimeoutMillis);
            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                //msg.target == null則該訊息為SyncBarrier訊息
                //排在SyncBarrier之後的Message中,只有設定了Asynchronous的Message會被處理
                //SyncBarrier的概念需要展開來講。
                if (msg != null && msg.target == null) {
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    //訊息設定了delay時間(when是將來執行的時間)。
                    //還沒到時間去處理,計算nextPollTimeoutMillis值,由nativePollOnce決定喚醒阻塞的時間
                    if (now < msg.when) {
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        //返回需要被處理的訊息,並刪除鏈節點
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    //沒有任何訊息,nextPollTimeoutMillis置成-1
                    nextPollTimeoutMillis = -1;
                }

                //quit()被呼叫後,退出迴圈
                if (mQuitting) {
                    dispose();
                    return null;
                }

                //獲取IdleHandler——列表中沒有訊息或者正等待超時期間會通知IdleHandler
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            //執行IdleHandler.queueIdle()
            //只有當第一次迴圈沒有返回Message時執行,就是說當前所有Message已經處理完
            //或者還沒到時間處理的時候。
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler
                boolean keep = false;
                try {
                    //通過返回值決定IdleHandler是否保留
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            //不再執行IdleHandler.queueIdle()
            pendingIdleHandlerCount = 0;

            //執行IdleHandler期間可能有訊息插入,因此回頭需要馬上喚醒nativePollOnce
            nextPollTimeoutMillis = 0;
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97

忽略IdleHandler和nextPollTimeoutMillis值的影響,大概流程如下: 
這裡寫圖片描述

nativePollOnce

nativePollOnce名字上理解應該是輪詢一次的意思,程式碼如下: 
android_os_MessageQueue.cpp在nativePollOnce方法中,呼叫了Looper的pollOnce方法:

/frameworks/base/core/jni/android_os_MessageQueue.cpp
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
        jlong ptr, jint timeoutMillis) {
    NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
    nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}

void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
    mPollEnv = env;
    mPollObj = pollObj;
    mLooper->pollOnce(timeoutMillis);
    mPollObj = NULL;
    mPollEnv = NULL;

    if (mExceptionObj) {
        env->Throw(mExceptionObj);
        env->DeleteLocalRef(mExceptionObj);
        mExceptionObj = NULL;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

流程進入到Looper,那麼先來從頭瞭解下它。

Native looper

在java層初始化MessageQueue的時候呼叫了nativeInit():

MessageQueue.java
    MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
    }
  • 1
  • 2
  • 3
  • 4
  • 5

nativeInit()初始化了NativeMessageQueue和Looper:

android_os_MessageQueue.cpp
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
    //建立NativeMessageQueue
    NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
    if (!nativeMessageQueue) {
        jniThrowRuntimeException(env, "Unable to allocate native queue");
        return 0;
    }

    nativeMessageQueue->incStrong(env);
    return reinterpret_cast<jlong>(nativeMessageQueue);
}

NativeMessageQueue::NativeMessageQueue() :
        mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
    mLooper = Looper::getForThread();
    if (mLooper == NULL) {
        //建立looper
        mLooper = new Looper(false);
        Looper::setForThread(mLooper);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

在Looper的建構函式中呼叫了rebuildEpollLocked(): 
1)初始化了epoll例項mEpollFd; 
2)註冊fd監聽——mWakeEventFd。

Looper::Looper(bool allowNonCallbacks) :
        mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
        mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false),
        mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
    mWakeEventFd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);

    AutoMutex _l(mLock);
    rebuildEpollLocked();
}

void Looper::rebuildEpollLocked() {
    // Close old epoll instance if we have one.
    if (mEpollFd >= 0) {
        close(mEpollFd);
    }

    //初始化了epoll例項
    mEpollFd = epoll_create(EPOLL_SIZE_HINT);

    struct epoll_event eventItem;
    memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
    eventItem.events = EPOLLIN;
    eventItem.data.fd = mWakeEventFd;
    //註冊fd用於喚醒——mWakeEventFd
    int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);

    //註冊其他fd
    for (size_t i = 0; i < mRequests.size(); i++) {
        const Request& request = mRequests.valueAt(i);
        struct epoll_event eventItem;
        request.initEventItem(&eventItem);

        int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, request.fd, & eventItem);
        ...
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

馬上回到pollOnce函式,它呼叫的pollInner函式中執行了epoll_wait,等待mWakeEventFd和其他註冊的fd被喚醒,然後分發Native訊息,等到函式返回後,Java層的MessageQueue.next()才繼續執行。

/system/core/libutils/Looper.cpp

int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
    ...
    result = pollInner(timeoutMillis);
}

int Looper::pollInner(int timeoutMillis) {
    //調整timeout時間
    if (timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) {
        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
        int messageTimeoutMillis = toMillisecondTimeoutDelay(now, mNextMessageUptime);
        if (messageTimeoutMillis >= 0
                && (timeoutMillis < 0 || messageTimeoutMillis < timeoutMillis)) {
            timeoutMillis = messageTimeoutMillis;
        }

    }

    ...
    //epoll_wait,等待喚醒或超時
    struct epoll_event eventItems[EPOLL_MAX_EVENTS];
    int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);

    ...

    for (int i = 0; i < eventCount; i++) {
        int fd = eventItems[i].data.fd;
        uint32_t epollEvents = eventItems[i].events;
        if (fd == mWakeEventFd) {
            if (epollEvents & EPOLLIN) {
                //清空mWakeEventFd管道
                awoken();
            } else {
                ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents);
            }
        } else {
            ...
        }
    }
Done: ;

    //Native層訊息的分發
    ...
    return result;
}

//清空mWakeEventFd管道
void Looper::awoken() {

    uint64_t counter;
    TEMP_FAILURE_RETRY(read(mWakeEventFd, &counter, sizeof(uint64_t)));
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

那麼喚醒這個epoll_wait的地方在哪?

nativeWake

android_os_MessageQueue.cpp的nativeWake函式,呼叫了Looper.cpp的wake()函式,向mWakeEventFd管道寫入了資料,epoll_wait被喚醒。

Looper.cpp
void Looper::wake() {
    uint64_t inc = 1;
    //向mWakeEventFd管道寫入資料
    ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
    ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

那麼問題又來了,何時去喚醒這個epoll? 答案在java層插入新訊息時,呼叫的MessageQueue.enqueueMessage()

插入新的Message

Handler通過post和sendMessage方法向MessageQueue傳送Runnable或者Message,實際上最後都會被封裝成Message,通過MessageQueue.enqueueMessage()方法加入到訊息連結串列。

Handler.java
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

MessageQueue.enqueueMessage方法接收新的訊息,通過訊息延遲的時間將其插入到正確的位置。

MessageQueue.java

Message mMessages;
boolean enqueueMessage(Message msg, long when) {
        ...
        synchronized (this) {
            if (mQuitting) {
                msg.recycle();
                return false;
            }

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                //新訊息作為連結串列頭
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                //如果找到sync barrier,且當前訊息是“非同步”的,那麼需要重新調整喚醒時間
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                //按照when取值將Message插入對應的位置
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    //下一個是沒有設定handler的“非同步”訊息,無需喚醒native looper
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            //喚醒native looper
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

插入訊息後,有條件地執行nativeWake去喚醒epoll。needWake的值依賴mBlocked——當進入next()方法時沒有待處理的訊息,mBlock為true,有訊息並返回到looper時,mBlock為false。

結合next()和enqueueMessage()方法,得知nativeWake被呼叫的條件為: 
1. next()方法在等待新訊息,且新插入訊息的為連結串列頭時。needWake為true 
2. 設定了Sync Barrier,且插入的訊息是“非同步”的。needWake為true

核心流程大概分析完成: 
1. java層的looper迴圈呼叫MessageQueue.next()獲取下一個訊息來處理; 
2. next()方法進入native層nativePollOnce方法,Looper.cpp進入epoll_wait等待fd被喚醒 
3. Handler向MessageQueue插入訊息後,有條件地喚醒native looper,使next()方法返回 
4. Looper在獲取到新訊息後分發處理。

關於SyncBarrier

首先,Message中有“同步”和“非同步”的概念(貌似實際上只是個狀態的區分,主要作用時配合SyncBarrier,並沒有同步性上的區別),使用setAsynchronous方法設定:

Message.java
    public void setAsynchronous(boolean async) {
        if (async) {
            flags |= FLAG_ASYNCHRONOUS;
        } else {
            flags &= ~FLAG_ASYNCHRONOUS;
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

而通過postSyncBarrier()方法,可以傳送一個synchronization barrier(就直譯稱為“同步欄”吧)到Message連結串列中,用來暫停在同步欄之後傳送的“同步”訊息的處理,此時只有“非同步”的訊息能被繼續處理。 設定同步欄後必須在條件準備好後移除它(呼叫removeSyncBarrier()方法)。

這個機制的作用在於可以馬上暫停執行“同步”訊息,直到條件允許後通過移除同步欄來恢復“同步”訊息的處理。例如在View.invalidate需要執行時,將會設定同步欄掛起所有“同步”訊息,直到下一幀準備好顯示後移除同步欄。

而設定“非同步”訊息則可以免受同步欄的影響,用於接收輸入等需要持續的工作,具體如下面這段註釋: 
/** 
* Asynchronous messages are exempt from synchronization barriers. They typically 
* represent interrupts, input events, and other signals that must be handled 
* independently even while other work has been suspended. 
* */ 
工作流程大致如圖: 
這裡寫圖片描述

設定同步欄就是插入一個不指定handler的Message,通過一個token值來標記:

MessageQueue.java
    /**
     * @hide
     */
    //設定同步欄
    //postSyncBarrier不是公開的API,只供系統內部呼叫
    public int postSyncBarrier() {
        return postSyncBarrier(SystemClock.uptimeMillis());
    }

    private int postSyncBarrier(long when) {
        synchronized (this) {
            final int token = mNextBarrierToken++;
            //訊息沒有設定handler,以此來識別這是個sync barrier
            final Message msg = Message.obtain();
            msg.markInUse();
            msg.when = when;
            msg.arg1 = token;
            //插入到正確的位置
            Message prev = null;
            Message p = mMessages;
            if (when != 0) {
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
            }
            if (prev != null) { // invariant: p == prev.next
                msg.next = p;
                prev.next = msg;
            } else {
                msg.next = p;
                mMessages = msg;
            }
            return token;
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

移除同步欄時通過token值匹配並刪除該Message:

    /**
     * @hide
     */
    //移除同步欄同樣是hide的方法
    public void removeSyncBarrier(int token) {
        // Remove a sync barrier token from the queue.
        // If the queue is no longer stalled by a barrier then wake it.
        synchronized (this) {
            Message prev = null;
            Message p = mMessages;
            while (p != null && (p.target != null || p.arg1 != token)) {
                prev = p;
                p = p.next;
            }
            if (p == null) {
                throw new IllegalStateException("The specified message queue synchronization "
                        + " barrier token has not been posted or has already been removed.");
            }
            final boolean needWake;
            if (prev != null) {
                prev.next = p.next;
                needWake = false;
            } else {
                mMessages = p.next;
                needWake = mMessages == null || mMessages.target != null;
            }
            p.recycleUnchecked();

            // If the loop is quitting then it is already awake.
            // We can assume mPtr != 0 when mQuitting is false.
            if (needWake && !mQuitting) {
                nativeWake(mPtr);
            }
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

關於IdleHandler

  1. 只有當前所有Message已經處理完或者待處理的Message還沒到時間處理的時候,才會執行IdleHandler處理一次
  2. 使用IdleHandler必須定義實現了IdleHandler介面的類,並在queueIdle()定義需要執行的操作(通常是釋放資源),返回值決定這個Handler是否一直保留,並在將來空閒時再次執行。
  3. 通過MessageQueue.addIdleHandler 新增IdleHandler
MessageQueue.java
    private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
    private SparseArray<FileDescriptorRecord> mFileDescriptorRecords;
    private IdleHandler[] mPendingIdleHandlers;

    public static interface IdleHandler {
        boolean queueIdle();
    }

    public void addIdleHandler(@NonNull IdleHandler handler) {
        if (handler == null) {
            throw new NullPointerException("Can't add a null IdleHandler");
        }
        synchronized (this) {
            mIdleHandlers.add(handler);
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

一個例子: 
ActivityThread中定義了一個IdleHandler,用於執行GC回收垃圾:

ActivityThread.java
    final class GcIdler implements MessageQueue.IdleHandler {
        @Override
        public final boolean queueIdle() {
            doGcIfNeeded();
            return false;
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

END

寫得有點雜亂,以後有時間再來優化下。 
在分析過程中,參考了下面幾篇文章,寫的比較清晰易懂: 
《聊一聊Android的訊息機制》 
《Looper中的睡眠等待與喚醒機制》