消息機制中主要用於多線程的通信,在 Android 開發中最多見的使用場景是:在子線程作耗時操做,操做完成後須要在主線程更新 UI(子線程不能直接修改 UI)。這時就須要用到消息機制來完成子線程和主線程的通信。java
如如下代碼片斷所示:數據結構
public class MainActivity extends AppCompatActivity {
private TextView tvText;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
tvText.setText(msg.obj.toString());
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvText = (TextView) findViewById(R.id.tv_text);
new Thread() {
@Override
public void run() {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Message msg = Message.obtain();
msg.obj = Thread.currentThread().getName();
mHandler.sendMessage(msg);
}
}.start();
}
}複製代碼
子線程阻塞 10 秒後發送消息更新 TextView,TextView 顯示來源的線程名。多線程
這裏有兩個限制:app
不能讓阻塞發生在主線程,不然會發生 ANR異步
不能在子線程更新 TextView。async
因此只能在子線程阻塞 10 秒,而後經過 Handler 發送消息,Handler 處理獲取到的消息並在主線程更新 TextView。ide
private Handler mHandler = new Handler() {
...
};複製代碼
查看 Handler 的源碼:oop
...
public Handler() {
this(null, false);
}
...
/** * @hide 該構造方法是隱藏的,沒法直接調用 */
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> 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();
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;
}
...複製代碼
Handler 的構造方法須要傳入兩個參數,第一個參數是 Handler.Callback 接口的實現,第二個參數是標誌傳遞的 Message 是不是異步。性能
構造方法內部首先會檢查 Handler 的使用是否可能存在內存泄漏的問題,若是存在會發出一個警告:優化
因此在使用 Handler 的時候通常聲明爲靜態內部類或使用弱引用的方式。
接着會調用 Looper.myLooper() 獲取到 Looper 對象,並判斷該 Looper 對象是否爲 null,若是爲 null 則拋出異常;若是不爲 null 則進行相應的賦值操做。由此可知 Looper.myLooper() 方法並不會構造一個 Looper 對象,而是從某個地方獲取到一個 Looper 對象。
因此,在建立 Handler 對象時必須先建立 Looper 對象。
下面查看 Looper 源碼:
...
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
...
/** 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 * {@link #loop()} after calling this method, and end it by calling * {@link #quit()}. */
public static void prepare() {
prepare(true);
}
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));
}
/** * 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. See also: {@link #prepare()} */
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
...
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
...
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
...複製代碼
從 Looper 的源碼可知,Looper 類中對外部提供兩個方法用於建立 Looper 對象:prepare() 和 prepareMainLooper(),並將建立的 Looper 對象保存到 sThreadLocal 中。myLooper() 方法獲取 Looper 對象也是從 sThreadLocal 中獲取。
sThreadLocal 是一個ThreadLocal 對象,ThreadLocal 用於提供線程局部變量,在多線程環境能夠保證各個線程裏的變量獨立於其它線程裏的變量。也就是說 ThreadLocal 能夠爲每一個線程建立一個【單獨的變量副本】,至關於一個線程的 private static 類型變量。
在 Looper 的真正建立對象方法 prepare(boolean quitAllowed) 中,會先判斷當前線程是否已經有 Looper 對象,沒有時才能夠建立並保存到當前線程中,每一個線程只容許有一個 Looper。
上面的示例代碼中是在主線程中實例化 Handler 的,可是並無調用 Looper 的建立方法,並且也沒有拋出異常,說明主線程中是有 Looper 對象的。
在 Looper 的 prepareMainLooper() 方法註釋中能夠看到這樣一句話:
The main looper for your application is created by the Android environment, so you should never need to call this function yourself.
意思是:應用程序的主 Looper 由 Android 環境建立,不該該本身調用該方法。
由此可知,在 Android 系統源碼中應該會調用該方法,經過查找該方法的使用發現,在 ActivityThread 類的 main 方法中調用了 Looper.prepareMainLooper():
public static void main(String[] args) {
...
Looper.prepareMainLooper();
...
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}複製代碼
因此在應用程序啓動時就已經爲主線程建立了一個 Looper 對象。
繼續分析示例代碼,在子線程阻塞結束後會建立一個 Message 對象,而後使用 Handler 發送該 Message 對象。
...
Message msg = Message.obtain();
msg.obj = Thread.currentThread().getName();
mHandler.sendMessage(msg);
...複製代碼
調用 Message 的 obtain() 方法建立 Message 對象。
查看 Message 的源碼:
...
public static Message obtain() {
synchronized (sPoolSync) {
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();
}
...
public Message() {
}
...複製代碼
Message 是消息機制中消息的載體,爲了優化性能,避免重複 Message 的建立,Message 使用了消息池機制,當調用 obtain() 方法時,會先嚐試從消息池中獲取一個 Message 對象,消息池中沒有時才建立新的對象;Message 對象使用完後會從新回收到消息池中。Message 的消息池使用了鏈表的數據結構,Message 類自己時支持鏈表結構的。
因此在建立 Message 對象時不要直接使用構造方法。
建立好 Message 對象後,能夠給 Message 的一些屬性賦值,用於描述該消息或攜帶數據。
調用 Handler 的 sendMessage(Message msg) 方法發送消息。
經過查看 Handler 的源碼可知,在 Handler 中有許多發送消息的方法,全部的發送消息方法最終都會調用 enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) 方法。
...
public final boolean sendMessage(Message msg) {
return sendMessageDelayed(msg, 0);
}
...
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
...
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);
}
...
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}複製代碼
首先給 Message 的 target 屬性賦值,即當前的 Handler;而後根據 Handler 的 mAsynchronous 值設置該 Message 是不是異步的,mAsynchronous 的值在 Handler 實例化時被賦值;最後調用 MessageQueue 的 enqueueMessage(Message msg, long when) 方法。
能夠看出在 Handler 中也只是對 Message 對象的屬性進行了相關賦值操做,最終是調用了 MessageQueue 的 enqueueMessage(Message msg, long when) 方法。
enqueueMessage() 方法中的 MessageQueue 對象來自於 Handler 的 mQueue 屬性:
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);
}複製代碼
而 mQueue 屬性在 Handler 實例化時賦值的:
public Handler(Callback callback, boolean async) {
...
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;
}複製代碼
mQueue 是 Looper 中的 MessageQueue 對象,在 Looper 建立時被實例化:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}複製代碼
查看 MessageQueue 的構造方法:
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}複製代碼
MessageQueue 是消息隊列,Handler 發送消息其實就是將 Message 對象插入到消息隊列中,該消息隊列也是使用了鏈表的數據結構。同時 Message 也是消息機制中 Java 層和 native 層的紐帶,這裏暫且不關心 native 層相關實現。
MessageQueue 在實例化時會傳入 quitAllowed 參數,用於標識消息隊列是否能夠退出,由 ActivityThread 中 Looper 的建立可知,主線程的消息隊列不能夠退出。
MessageQueue 插入消息:
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) { // target 即 Handler 不容許爲 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(); // 回收 Message,回收到消息池
return false;
}
msg.markInUse(); // 標記爲正在使用
msg.when = when;
Message p = mMessages; // 獲取當前消息隊列中的第一條消息
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// 消息隊列爲空 或 新消息的觸發時間爲 0 或 新消息的觸發時間比消息隊列的第一條消息的觸發時間早
// 將新消息插入到隊列的頭,做爲消息隊列的第一條消息。
msg.next = p;
mMessages = msg;
needWake = mBlocked; // 當阻塞時須要喚醒
} else {
// 將新消息插入到消息隊列中(非隊列頭)
// 當阻塞 且 消息隊列頭是 Barrier 類型的消息(消息隊列中一種特殊的消息,能夠看做消息屏障,用於攔截同步消息,放行異步消息) 且 當前消息是異步的 時須要喚醒
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
// 循環消息隊列,比較新消息的觸發時間和隊列中消息的觸發時間,將新消息插入到合適的位置
for (;;) {
prev = p; // 將前一條消息賦值給 prev
p = p.next; // 將下一條消息賦值給 p
if (p == null || when < p.when) {
// 若是已是消息隊列中的最後一條消息 或 新消息的觸發時間比較早 則退出循環
break;
}
if (needWake && p.isAsynchronous()) {
// 須要喚醒 且 下一條消息是異步的 則不須要喚醒
needWake = false;
}
}
// 將新消息插入隊列
msg.next = p;
prev.next = msg;
}
if (needWake) {
// 若是須要喚醒調用 native 方法喚醒
nativeWake(mPtr);
}
}
return true;
}複製代碼
MessageQueue 根據消息的觸發時間,將新消息插入到合適的位置,保證全部的消息的時間順序。
消息的發送已經分析過了,下面須要分析的是如何獲取消息並處理消息,繼續分析實例代碼:
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
tvText.setText(msg.obj.toString());
}
};複製代碼
從示例代碼中能夠看到 Handler 的 handleMessage(Message msg) 負責處理消息,可是並無看到是如何獲取到消息的。須要在 Handler 的源碼中查找是在哪裏調用 handleMessage(Message msg) 方法的。
經過在 Handler 的源碼中查找,發現是在 dispatchMessage(Message msg) 方法中調用 handleMessage(Message msg) 的。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}複製代碼
dispatchMessage(Message msg) 方法中,根據不一樣的狀況調用不一樣的消息處理方法。繼續向上查找 dispatchMessage(Message msg) 的引用,發現是在 Looper 的 loop() 方法中調用的,而在以前分析 Looper 的建立時,能夠知道在 ActivityThread 的 main 方法中有調用 loop() 方法。
public static void main(String[] args) {
...
Looper.prepareMainLooper();
...
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}複製代碼
下面分析 loop() 方法:
public static void loop() {
final Looper me = myLooper(); // 獲取當前線程的 Looper 對象
if (me == null) { // 當前線程沒有 Looper 對象則拋出異常
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue; // 獲取到當前線程的消息隊列
// 清空遠程調用端進程的身份,用本地進程的身份代替,確保此線程的身份是本地進程的身份,並跟蹤該身份令牌
// 這裏主要用於保證消息處理是發生在當前 Looper 所在的線程
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
// 無限循環
for (;;) {
Message msg = queue.next(); // 從消息隊列中獲取消息,可能會阻塞
if (msg == null) {
// 沒有消息則退出循環,正常狀況下不會退出的,只會阻塞在上一步,直到有消息插入並喚醒返回消息
return;
}
// 默認爲 null,可經過 setMessageLogging() 方法來指定輸出,用於 debug 功能
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
// 開始跟蹤,並寫入跟蹤消息,用於 debug 功能
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
// 經過 Handler 分發消息
msg.target.dispatchMessage(msg);
} finally {
// 中止跟蹤
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// 確保在分發消息的過程當中線程的身份沒有改變,若是改變則發出警告
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(); // 回收消息,將 Message 放入消息池
}
}複製代碼
在 loop() 方法中,會不停的循環如下操做:
調用當前線程的 MessageQueue 對象的 next() 方法獲取消息
經過消息的target,即 Handler 分發消息
回收消息,將分發後的消息放入消息池
在 loop() 方法中獲取消息時有可能會阻塞,來看下 MessageQueue 的 next() 方法的實現:
Message next() {
// 若是消息隊列退出,則直接返回
// 正常運行的應用程序主線程的消息隊列是不會退出的,一旦退出則應用程序就會崩潰
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // 記錄空閒時處理的 IdlerHandler 數量,可先忽略
int nextPollTimeoutMillis = 0; // native 層使用的變量,設置的阻塞超時時長
// 開始循環獲取消息
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// 調用 native 方法阻塞,當等待nextPollTimeoutMillis時長,或者消息隊列被喚醒,都會中止阻塞
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// 嘗試獲取下一條消息,獲取到則返回該消息
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages; // 獲取消息隊列中的第一條消息
if (msg != null && msg.target == null) {
// 若是 msg 爲 Barrier 類型的消息,則攔截全部同步消息,獲取第一個異步消息
// 循環獲取第一個異步消息
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// 若是 msg 的觸發時間尚未到,設置阻塞超時時長
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 獲取消息並返回
mBlocked = false;
if (prevMsg != null) {
// 若是 msg 不是消息隊列的第一條消息,上一條消息的 next 指向 msg 的 next。
prevMsg.next = msg.next;
} else {
// 若是 msg 是消息隊列的第一條消息,則 msg 的 next 做爲消息隊列的第一條消息 // msg 的 next 置空,表示從消息隊列中取出了 msg。
mMessages = msg.next;
}
msg.next = null; // msg 的 next 置空,表示從消息隊列中取出了 msg
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse(); // 標記 msg 爲正在使用
return msg; // 返回該消息,退出循環
}
} else {
// 若是沒有消息,則設置阻塞時長爲無限,直到被喚醒
nextPollTimeoutMillis = -1;
}
// 若是消息正在退出,則返回 null
// 正常運行的應用程序主線程的消息隊列是不會退出的,一旦退出則應用程序就會崩潰
if (mQuitting) {
dispose();
return null;
}
// 第一次循環 且 (消息隊列爲空 或 消息隊列的第一個消息的觸發時間尚未到)時,表示處於空閒狀態
// 獲取到 IdleHandler 數量
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// 沒有 IdleHandler 須要運行,循環並等待
mBlocked = true; // 設置阻塞狀態爲 true
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// 運行 IdleHandler,只有第一次循環時纔會運行
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // 釋放 IdleHandler 的引用
boolean keep = false;
try {
keep = idler.queueIdle(); // 執行 IdleHandler 的方法
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler); // 移除 IdleHandler
}
}
}
// 重置 IdleHandler 的數量爲 0,確保不會重複運行
// pendingIdleHandlerCount 置爲 0 後,上面能夠經過 pendingIdleHandlerCount < 0 判斷是不是第一次循環,不是第一次循環則 pendingIdleHandlerCount 的值不會變,始終爲 0。
pendingIdleHandlerCount = 0;
// 在執行 IdleHandler 後,可能有新的消息插入或消息隊列中的消息到了觸發時間,因此將 nextPollTimeoutMillis 置爲 0,表示不須要阻塞,從新檢查消息隊列。
nextPollTimeoutMillis = 0;
}
}複製代碼
nativePollOnce(ptr, nextPollTimeoutMillis) 是調用 native 層的方法執行阻塞操做,其中 nextPollTimeoutMillis 表示阻塞超時時長:
nextPollTimeoutMillis = 0 則不阻塞
nextPollTimeoutMillis = -1 則一直阻塞,除非消息隊列被喚醒
消息機制的流程以下:
準備階段:
在子線程調用 Looper.prepare() 方法或 在主線程調用 Lopper.prepareMainLooper() 方法建立當前線程的 Looper 對象(主線程中這一步由 Android 系統在應用啓動時完成)
在建立 Looper 對象時會建立一個消息隊列 MessageQueue
Looper 經過 loop() 方法獲取到當前線程的 Looper 並啓動循環,從 MessageQueue 不斷提取 Message,若 MessageQueue 沒有消息,處於阻塞狀態
發送消息
使用當前線程建立的 Handler 在其它線程經過 sendMessage() 發送 Message 到 MessageQueue
MessageQueue 插入新 Message 並喚醒阻塞
獲取消息
從新檢查 MessageQueue 獲取新插入的 Message
Looper 獲取到 Message 後,經過 Message 的 target 即 Handler 調用 dispatchMessage(Message msg) 方法分發提取到的 Message,而後回收 Message 並繼續循環獲取下一個 Message
Handler 使用 handlerMessage(Message msg) 方法處理 Message
阻塞等待