Android開發中經常用到handler來發送消息,比如将消息抛到主線程去更新UI。handler機制涉及到四個核心的類:
- Looper:消息循環,有一個Messagequenue,不斷從消息隊列中取出消息;
- MessageQuenue:消息隊列,裡邊包含Message;
- Message:消息,裡邊有一個Handler,負責處理該消息;
- Handler:管理消息隊列,裡邊有Looper和MessageQueue;
為了更好的了解這三者的關系,我們可以想象成現實生活中,工廠裡邊的一條流水線。有一個放産品(Message)的傳送帶(MessageQuenue),被機器帶動起來不斷循環運作(Looper),而勞工(Handler)需要将産品放到傳送帶上,然後傳送到指定位置時再将産品進行下一步處理。
從上述表達的關系可以看出,有一個MessageQuenue,可以往裡邊仍Message,而Looper則要讓MessageQuenue轉起來,不斷從裡邊取消息;而Handler負責把Message放到隊列裡邊,最後再處理這個消息。
注:以下源碼基于Android 9.0(版本名稱: Pie API Level: 28)。
Looper類分析
frameworks/base/core/java/android/os/Looper.java
在該檔案中給我們提供了一個執行個體:
* <pre>
* class LooperThread extends Thread {
* public Handler mHandler;
*
* public void run() {
* // ①調用prepare
* Looper.prepare();
*
* mHandler = new Handler() {
* public void handleMessage(Message msg) {
* // process incoming messages here
* }
* };
* // ② 進入消息循環
* Looper.loop();
* }
* }</pre>
上述使用了Looper的兩個關鍵調用(①和②),下面逐一分析。
Looper準備:
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
// 一個Looper隻能調用一次prepare
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 構造一個Looper對象,設定到調用線程的局部變量中
sThreadLocal.set(new Looper(quitAllowed));
}
// sThreadLocal定義
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
ThreadLocal是一個泛型,是Java線程中的局部變量。全名是Thread Local Variable。具體可以自行研究源碼:libcore/ojluni/src/main/java/java/lang/ThreadLocal.java
Looper構造函數:
private Looper(boolean quitAllowed) {
// 構造一個消息隊列
mQueue = new MessageQueue(quitAllowed);
// 擷取目前線程的Thread對象
mThread = Thread.currentThread();
}
prepare主要建立了Looper對象,并儲存在調用線程的ThreadLocal中。同時Looper對象内部封裝了一個消息隊列。prepare通過ThreadLocal機制将Looper和調用線程關聯。Looper循環:
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
// 傳回儲存在調用線程的TLV中的Looper對象。
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
// 取出Looper的消息隊列
final MessageQueue queue = me.mQueue;
// ... 細節省略
for (; ; ) {
// 從消息隊列中取出消息
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// ... 細節省略
try {
// 調用該消息的Handler,交給它的dispatchMessage函數處理
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
// ... 細節省略
msg.recycleUnchecked();
}
}
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*
*/
public static @Nullable
Looper myLooper() {
return sThreadLocal.get();
}
通過上面源碼分析可以了解到Looper的作用:
- 封裝一個消息隊列(Messagequenue);
- Looper的prepare函數将Looper和調用prepare的線程(即處理線程)綁定;
- loop函數啟動一個無線循環來處理消息隊列中的消息。
當事件源向這個Looper發送消息的時候,其實是把消息加到這個Looper的消息隊列裡了。那麼,該消息就将由和Looper綁定的處理線程來處理。可事件源又是怎麼向Looper消息隊列添加消息的呢?下面需要了解Looper、Message、Handler的關系。
- Looper中有一個Message隊列,裡邊存儲的是一個個待處理的Message;
- Message中有一個Handler,這個Handler是用來處理Message的。
其中Handler類封裝了很多瑣碎的工作。具體:
frameworks/base/core/java/android/os/Handler.java
final Looper mLooper; // 有一個Looper
final MessageQueue mQueue; // 有一個消息隊列
final Callback mCallback; // 有一個回調用的類
final boolean mAsynchronous; // 異步相關設定,本文不做分析
Handler有幾個構造函數,主要差別是對上面三個重要成員變量初始化上。
// 構造函數1
public Handler() {
this(null, false);
}
// 構造函數2
public Handler(Callback callback) {
this(callback, false);
}
// 構造函數3
public Handler(Looper looper) {
this(looper, null, false);
}
// 構造函數4
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
// 構造函數5
public Handler(boolean async) {
this(null, async);
}
public Handler(Callback callback, boolean async) {
// ... 細節省略
// 獲得調用線程的Looper
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
// 得到Looper的消息隊列
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
上述構造函數中,Handler中的消息隊列變量最終都會指向Looper的消息隊列,Handler提供了一系列函數,幫助我們完成建立消息和插入消息隊列的工作。這裡列出一部分分析:
// 檢視消息隊列中是否有消息碼是what的消息
public final boolean hasMessages(int what)
// 從Handler中建立一個消息碼是what的消息
public final Message obtainMessage(int what)
// 從消息隊列中移除消息碼是what的消息
public final void removeMessages(int what)
// 發送一個隻填充了消息碼的消息
public final boolean sendEmptyMessage(int what)
// 發送一個消息,該消息添加到隊列尾
public final boolean sendMessage(Message msg)
// 發送一個消息,該消息添加到隊列頭,是以優先級很高
public final boolean sendMessageAtFrontOfQueue(Message msg)
以sendMessage為了,其代碼實作如下所示:
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) {
// 把Message的target設為自己,然後加入到消息隊列
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
Handler的消息處理
剛才,我們往Looper的消息隊列中加入了一個消息,按照Looper的處理規則,它在擷取消息後會調用target的dispatchMessage函數,再把這個消息派發給Handler處理。
public void dispatchMessage(Message msg) {
// 如果Message本身有callback,則直接交給Message的callback處理
if (msg.callback != null) {
handleCallback(msg);
} else {
// 如果本Handler設定mCallback,則交給mCallback處理
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
// 最後才交給子類處理
handleMessage(msg);
}
}
通常情況下,我們一般都是采用第三種方法,即在子類中通過重載handleMessage來完成處理工作。
要了解Handler機制,主要是弄清楚Looper、MessageQuenue、Message、Handler之間的關系。為了便于了解,我們可以把這套架構想象成現實生活中工廠流水線,勞工(Handler)拿到産品(Message),放在傳送帶(MessageQuenue)上,同時做了一個标記(msg.target = this;),傳送帶由機器帶動(Looper),然後當機器将産品傳遞到另一位置時,該員工根據産品上的标記,負責處理該産品,分發到不同的地方。
上述分析細節可以自行檢視Android源碼,推薦網站:http://www.androidos.net.cn/sourcecode
擴充:
Looper類裡邊啟動了一個for(;;)循環,如果消息隊列時會不會一直占用CPU時間片?
循環中queue.next()函數裡邊用到了pipe/epoll機制,確定不會占用CPU時間片。之後會通過源碼進行分析。