Android Handler機制系列文章整體內容如下:
- Android Handler機制1之Thread
- Android Handler機制2之ThreadLocal
- Android Handler機制3之SystemClock類
- Android Handler機制4之Looper與Handler簡介
- Android Handler機制5之Message簡介與消息對象對象池
- Android Handler機制6之MessageQueue簡介
- Android Handler機制7之消息發送
- Android Handler機制8之消息的取出與消息的其他操作
- Android Handler機制9之Handler的Native實現前奏之Linux IO多路復用
- Android Handler機制10之Handdler的Native實現Native的實現
- Android Handler機制11之Handler機制總結
- Android Handler機制12之Callable、Future和FutureTask
- Android Handler機制13之AsyncTask源碼解析
本片文章的主要內容如下:
- 1、Handler發送消息
- 2、Handler的send方案
- 3、Handler的post方案
一 、Handler發送消息
大家平時發送消息主要是調用的兩大類方法
如下兩圖:
光看上面這些API你可能會覺得handler能法兩種消息,一種是Runnable對象,一種是message對象,這是直觀的理解,但其實post發出的Runnable對象最后都封裝成message對象了。
- send方案發送消息(需要回調才能接收消息)
- 1、sendMessage(Message) 立即發送Message到消息隊列
- 2、sendMessageAtFrontOfQueue(Message) 立即發送Message到隊列,而且是放在隊列的最前面
- 3、sendMessageAtTime(Message,long) 設置時間,發送Message到隊列
- 4、sendMessageDelayed(Message,long) 延時若干毫秒后,發送Message到隊列
- post方案 立即發送Message到消息隊列
- 1、post(Runnable) 立即發送Message到消息隊列
- 2、postAtFrontOfQueue(Runnable) 立即發送Message到隊列,而且是放在隊列的最前面
- 3、postAtTime(Runnable,long) 設置時間,發送Message到隊列
- 4、postDelayed(Runnable,long) 在延時若干毫秒后,發送Message到隊列
下面我們就先從send方案中的第一個sendMessage() 開始源碼跟蹤下:
二、 Handler的send方案
我們以Handler的sendMessage(Message msg)為例子。
(一)、boolean sendMessage(Message msg)方法
代碼在Handler.java 505行
/**
* Pushes a message onto the end of the message queue after all pending messages
* before the current time. It will be received in {@link #handleMessage},
* in the thread attached to this handler.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
老規矩先翻譯一下注釋:
在當前時間,在所有待處理消息之后,將消息推送到消息隊列的末尾。在和當前線程關聯的的Handler里面的handleMessage將收到這條消息,
我們看到sendMessage(Message)里面代碼很簡單,就是調用了sendMessageDelayed(msg,0)
1、boolean sendMessageDelayed(Message msg, long delayMillis)
代碼在Handler.java 565行
/**
* Enqueue a message into the message queue after all pending messages
* before (current time + delayMillis). You will receive it in
* {@link #handleMessage}, in the thread attached to this handler.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the message will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
注釋和boolean sendMessage(Message msg)方法差不多,我就不翻譯了
該方法內部就做了兩件事
- 1、判斷delayMillis是否小于0
- 2、調用了public boolean sendMessageAtTime(Message msg, long uptimeMillis)方法
2、boolean sendMessageAtTime(Message msg, long uptimeMillis)
代碼在Handler.java 592行
/**
* Enqueue a message into the message queue after all pending messages
* before the absolute time (in milliseconds) <var>uptimeMillis</var>.
* <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
* Time spent in deep sleep will add an additional delay to execution.
* You will receive it in {@link #handleMessage}, in the thread attached
* to this handler.
*
* @param uptimeMillis The absolute time at which the message should be
* delivered, using the
* {@link android.os.SystemClock#uptimeMillis} time-base.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the message will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/
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);
}
老規矩先翻譯一下注釋:
以android系統的SystemClock的uptimeMillis()為基準,以毫秒為基本單位的絕對時間下,在所有待處理消息后,將消息放到消息隊列中。深度睡眠中的時間將會延遲執行的時間,你將在和當前線程辦的規定的Handler中的handleMessage中收到該消息。
這里順便提一下異步的作用,因為通常我們理解的異步是指新開一個線程,但是這里不是,因為異步的也是發送到looper所綁定的消息隊列中,這里的異步主要是針對Message中的障柵(Barrier)而言的,當出現障柵(Barrier)的時候,同步的會被阻塞,而異步的則不會。所以這個異步僅僅是一個標記而已。
該方法內部就做了兩件事
- 1、獲取消息隊列,并對該消息隊列做非空判斷,如果為null,直接返回false,表示消息發送失敗
- 2、調用了boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)方法
3、boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)
代碼在Handler.java 626行
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
本方法內部做了三件事
- 1、設置msg的target變量,并將target指向自己
- 2、如果Handler的mAsynchronous值為true(默認為false,即不設置),則設置msg的flags值,讓是否異步在Handler和Message達成統一。
- 3、調用MessageQueue的enqueueMessage()方法
4、boolean enqueueMessage(Message msg, long when)方法
代碼在MessageQueue.java 533行
boolean enqueueMessage(Message msg, long when) {
//第一步
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
// 第二步
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
// 第三步
synchronized (this) {
// 第四步
//判斷消息隊列是否正在關閉
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
// 第五步
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
// 第六步
//根據when的比較來判斷要添加的Message是否應該放在隊列頭部,當第一個添加消息的時候,
// 測試隊列為空,所以該Message也應該位于頭部。
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
// 把msg的下一個元素設置為p
msg.next = p;
// 把msg設置為鏈表的頭部元素
mMessages = msg;
// 如果有阻塞,則需要喚醒
needWake = mBlocked;
} else {
// 第七步
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
//除非消息隊列的頭部是障柵(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;
}
// 第十一 步
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
// 第十二步
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// 第十三步
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
// 第十四步
return true;
}
因為這是消息入隊的流程,為了讓大家更好的理解,我將上面的流程大致分為12步驟
- 第1步驟、 判斷msg的target變量是否為null,如果為null,則為障柵(barrier),而障柵(barrier)入隊則是通過postSyncBarrier()方法入隊,所以msg的target一定有值
- 第2步驟、 判斷msg的標志位,因為此時的msg應該是要入隊,意味著msg的標志位應該顯示還未被使用。如果顯示已使用,明顯有問題,直接拋異常。
- 第3步驟、 加入同步鎖。
- 第4步驟、 判斷消息隊列是否正在被關閉,如果是正在被關閉,則return false告訴消息入隊是失敗,并且回收消息
- 第5步驟、 設置msg的when并且修改msg的標志位,msg標志位顯示為已使用
- 第6步驟、 如果p==null則說明消息隊列中的鏈表的頭部元素為null;when == 0 表示立即執行;when< p.when 表示 msg的執行時間早與鏈表中的頭部元素的時間,所以上面三個條件,那個條件成立,都要把msg設置成消息隊列中鏈表的頭部是元素
- 第7步驟、 如果上面三個條件都不滿足則說明要把msg插入到中間的位置,不需要插入到頭部
- 第8步驟、 如果頭部元素不是障柵(barrier)或者異步消息,而且還是插入中間的位置,我們是不喚醒消息隊列的。
- 第9步驟、 進入一個死循環,將p的值賦值給prev,前面的帶我們知道,p指向的是mMessage,所以這里是將prev指向了mMessage,在下一次循環的時候,prev則指向了第一個message,一次類推。接著講p指向了p.next也就是mMessage.next,也就是消息隊列鏈表中的第二個元素。這一步驟實現了消息指針的移動,此時p表示的消息隊列中第二個元素。
- 第10步驟、 p==null,則說明沒有下一個元素,即消息隊列到頭了,跳出循環;p!=null&&when < p.when 則說明當前需要入隊的這個message的執行時間是小于隊列中這個任務的執行時間的,也就是說這個需要入隊的message需要比隊列中這個message先執行,則說明這個位置剛剛是適合這個message的,所以跳出循環。 如果上面的兩個條件都不滿足,則說明這個位置還不是放置這個需要入隊的message,則繼續跟鏈表中后面的元素,也就是繼續跟消息隊列中的下一個消息進行對比,直到滿足條件或者到達隊列的末尾。
- 第11步驟、 因為沒有滿足條件,說明隊列中還有消息,不需要喚醒。
- 第12步驟、 跳出循環后主要做了兩件事:事件A,將入隊的這個消息的next指向循環中獲取到的應該排在這個消息之后message。事件B,將msg前面的message.next指向了msg。這樣就將一個message完成了入隊。
- 第13步驟、 如果需要喚醒,則喚醒,具體請看后面的Handler中的Native詳解。
- 第14步驟、 返回true,告知入隊成功。
提供兩張圖,讓大家更好的理解入隊
總結一句話就是:就是遍歷消息隊列中所有的消息,根據when的比較找到合適添加Message的位置。
上面是sendMessage(Message msg)發送消息機制,這樣再來看下其他的send方案
(二) boolean sendMessageAtFrontOfQueue(Message msg)
代碼在Handler.java 615行
/**
* Enqueue a message at the front of the message queue, to be processed on
* the next iteration of the message loop. You will receive it in
* {@link #handleMessage}, in the thread attached to this handler.
* <b>This method is only for use in very special circumstances -- it
* can easily starve the message queue, cause ordering problems, or have
* other unexpected side-effects.</b>
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean sendMessageAtFrontOfQueue(Message msg) {
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, 0);
}
老規矩先翻譯一下注釋:
在消息隊列的最前面插入一個消息,在消息循環的下一次迭代中進行處理。你將在當前線程關聯的Handler的handleMessage()中收到這個消息。由于它可以輕松的解決消息隊列的排序問題和其他的意外副作用。
方法內部的實現和boolean sendMessageAtTime(Message msg, long uptimeMillis)大體上一致,唯一的區別就是該方法在調用enqueueMessage(MessageQueue, Message, long)方法的時候,最后一個參數是0而已。
(三) boolean sendEmptyMessage(int what)
代碼在Handler.java 517行
/**
* Sends a Message containing only the what value.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean sendEmptyMessage(int what)
{
return sendEmptyMessageDelayed(what, 0);
}
老規矩先翻譯一下注釋:
發送一個僅有what的Message
內容很簡單,也就是調用sendEmptyMessageDelayed(int,long)而已,那么下面我們來看下sendEmptyMessageDelayed(int,long)的具體實現。
1、boolean sendEmptyMessageDelayed(int what, long delayMillis)
代碼在Handler.java 531行
/**
* Sends a Message containing only the what value, to be delivered
* after the specified amount of time elapses.
* @see #sendMessageDelayed(android.os.Message, long)
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
老規矩先翻譯一下注釋:
發送一個僅有what的Message,并且延遲特定的時間發送
這個方法內部主要就是做了3件事
- 1、調用Message.obtain();從消息對象池中獲取一個空的Message。
- 2、設置這個Message的what值
- 3、調用sendMessageDelayed(Message,long) 將這個消息方法
sendMessageDelayed(Message,long) 這個方法上面有講解過,這里就不詳細說了
(四) boolean sendEmptyMessageAtTime(int what, long uptimeMillis)
代碼在Handler.java 547行
/**
* Sends a Message containing only the what value, to be delivered
* at a specific time.
* @see #sendMessageAtTime(android.os.Message, long)
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageAtTime(msg, uptimeMillis);
}
老規矩先翻譯一下注釋:
發送一個僅有what的Message,并且在特定的時間發送
這個方法內部主要就是做了3件事
- 1、調用Message.obtain();從消息對象池中獲取一個空的Message。
- 2、設置這個Message的what值
- 3、調用sendMessageAtTime(Message,long) 將這個消息方法
(五) 小結
綜上所述
- public final boolean sendMessage(Message msg)
- public final boolean sendEmptyMessage(int what)
- public final boolean sendEmptyMessageDelayed(int what, long delayMillis)
- public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis)
- public final boolean sendMessageDelayed(Message msg, long delayMillis)
- public boolean sendMessageAtTime(Message msg, long uptimeMillis)
- public boolean sendMessageAtTime(Message msg, long uptimeMillis)
- public final boolean sendMessageAtFrontOfQueue(Message msg)
以上這些send方案都會從這里或者那里最終走到boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)如下圖
三、 Handler的post方案
(一)、boolean post(Runnable r)方法
代碼在Handler.java 324行
/**
* Causes the Runnable r to be added to the message queue.
* The runnable will be run on the thread to which this handler is
* attached.
*
* @param r The Runnable that will be executed.
*
* @return Returns true if the Runnable was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
老規矩先翻譯一下注釋:
將一個Runnable添加到消息隊列中,這個runnable將會在和當前Handler關聯的線程中被執行。
這個方法內部很簡單,就是調用了sendMessageDelayed(Message, long);這個方法,所以可見boolean post(Runnable r)這個方法最終還是走到上面說到的send的流程中,最終調用boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)。
這里面調用了Message getPostMessage(Runnable r),我們來看一下。
1、Message getPostMessage(Runnable r)方法
代碼在Handler.java 725行
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
代碼很簡單,主要是做了兩件事
- 通過Message.obtain()從消息對象池中獲取一個空的Message
- 將這空的Message的callback變量指向Runnable
最后返回這個Message m。
2、小結
所以我們知道boolean post(Runnable r)方法的內置也是通過Message.obtain()來獲取一個Message對象m,然后僅僅把m的callback指向參數r而已。最后最終通過調用send方案的某個流程最終調用到boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)
這時候聰明的同學一定會想,那么post方案的其他方法是不是也是這樣的?是的,的確都是這樣,這都是“套路”,那我們就用一一去檢驗。
(二)、boolean postAtTime(Runnable r, long uptimeMillis)方法
代碼在Handler.java 347行
/**
* Causes the Runnable r to be added to the message queue, to be run
* at a specific time given by <var>uptimeMillis</var>.
* <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
* Time spent in deep sleep will add an additional delay to execution.
* The runnable will be run on the thread to which this handler is attached.
*
* @param r The Runnable that will be executed.
* @param uptimeMillis The absolute time at which the callback should run,
* using the {@link android.os.SystemClock#uptimeMillis} time-base.
*
* @return Returns true if the Runnable was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the Runnable will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/
public final boolean postAtTime(Runnable r, long uptimeMillis)
{
return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
老規矩先翻譯一下注釋:
將一個Runnable添加到消息隊列中,這個runnable將會一個特定的時間被執行,這個時間是以android.os.SystemClock.uptimeMillis()為基準。如果在深度睡眠下,會推遲執行的時間,這個Runnable將會在和當前Hander關聯的線程中被執行。
方法內部也是先是調用getPostMessage(Runnable)來獲取一個Message,這個Message的callback字段指向了這個Runnable,然后調用sendMessageAtTime(Message,long)。
(三)、boolean postAtTime(Runnable r, Object token, long uptimeMillis)方法
代碼在Handler.java 372行
/**
* Causes the Runnable r to be added to the message queue, to be run
* at a specific time given by <var>uptimeMillis</var>.
* <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
* Time spent in deep sleep will add an additional delay to execution.
* The runnable will be run on the thread to which this handler is attached.
*
* @param r The Runnable that will be executed.
* @param uptimeMillis The absolute time at which the callback should run,
* using the {@link android.os.SystemClock#uptimeMillis} time-base.
*
* @return Returns true if the Runnable was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the Runnable will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*
* @see android.os.SystemClock#uptimeMillis
*/
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
{
return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}
這個方法的翻譯同上,這個方法和上個方法唯一不同就是多了Object參數,而這個參數僅僅是把Message.obtain();獲取的Message的obj字段的指向第二個入參token而已。最后也是調用sendMessageAtTime(Message,long)。
(四)、boolean postDelayed(Runnable r, long delayMillis)方法
代碼在Handler.java 396行
/**
* Causes the Runnable r to be added to the message queue, to be run
* after the specified amount of time elapses.
* The runnable will be run on the thread to which this handler
* is attached.
* <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
* Time spent in deep sleep will add an additional delay to execution.
*
* @param r The Runnable that will be executed.
* @param delayMillis The delay (in milliseconds) until the Runnable
* will be executed.
*
* @return Returns true if the Runnable was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the Runnable will be processed --
* if the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/
public final boolean postDelayed(Runnable r, long delayMillis)
{
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
這個方法也很簡單,就是依舊是通過getPostMessage(Runnable)來獲取一個Message,最后調用sendMessageDelayed(Message,long)而已。
(五)、boolean postDelayed(Runnable r, long delayMillis)方法
代碼在Handler.java 416行
/**
* Posts a message to an object that implements Runnable.
* Causes the Runnable r to executed on the next iteration through the
* message queue. The runnable will be run on the thread to which this
* handler is attached.
* <b>This method is only for use in very special circumstances -- it
* can easily starve the message queue, cause ordering problems, or have
* other unexpected side-effects.</b>
*
* @param r The Runnable that will be executed.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean postAtFrontOfQueue(Runnable r)
{
return sendMessageAtFrontOfQueue(getPostMessage(r));
}
這個方法也很簡單,就是依舊是通過getPostMessage(Runnable)來獲取一個Message,最后調用sendMessageAtFrontOfQueue(Message)而已。
(六)、小結
Handler的post方案的如下方法
- boolean post(Runnable r)
- postAtTime(Runnable r, long uptimeMillis)
- boolean postAtTime(Runnable r, Object token, long uptimeMillis)
- boolean postDelayed(Runnable r, long delayMillis)
- boolean postAtFrontOfQueue(Runnable r)
都是通過Message getPostMessage(Runnable )中調用Message m = Message.obtain();來獲取一個空的Message,然后把這個Message的callback變量指向了Runnable,最終調用相應的send方案而已。
所以我們可以這樣說:
Handler的發送消息(障柵除外),無論是通過send方案還是pos方案最終都會做走到 boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) 中