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處理。
- 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
- 只有當前所有Message已經處理完或者待處理的Message還沒到時間處理的時候,才會執行IdleHandler處理一次。
- 使用IdleHandler必須定義實現了IdleHandler介面的類,並在queueIdle()定義需要執行的操作(通常是釋放資源),返回值決定這個Handler是否一直保留,並在將來空閒時再次執行。
- 通過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中的睡眠等待與喚醒機制》