我們使用Handler其實就是就是2個功能:
- 發送消息;
- 處理消息;
但是在這之前 ,我們要做好準備工作,那就是必須得有Looper和MessageQueue才行。
Handler就類似一個快遞站,如果你沒有對應的 運輸設備(Looper) 和存儲倉庫(MessageQueue),就沒辦法運行啊。
1. 使用Handler的前提條件
通過源碼,我們發現Handler其實有很多構造函數,但是最終都指向一個:
public Handler(@Nullable Callback callback, boolean async) {
// 此處省略監聽 內存泄漏 的代碼...
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
我們可以創建Handler都是要先獲取到Looper 和 MessageQueue 的.
2. Handler發送消息
消息的發送主要分爲三種發送:
- 普通發送;
- 定時發送;
- 延時發送;
2.1 send方式
這些是Handler類關於sendMessage的方法。
不得不感慨一下,如果使用Kotlin重寫Handler,就不會存在這麼多的重載方法啦。
那麼Handler是如何處理三者(普通,定時,延時)的關係呢?這裏不妨可以思考一下…
具體看一下源碼的設計,驗證一下我們的猜想:
2.1.1. 普通發送:sendMessage()
通過延時發送(sendMessageDelayed),設置延時爲0來實現即時發送的。
public final boolean sendMessage(@NonNull Message msg) {
// 注意這裏,通過調用延時0發送,即:立刻發送
return sendMessageDelayed(msg, 0);
}
2.1.2. 延時發送:sendMessageDelayed()
具體的通過定時發送(sendMessageAtTime) 來實現的延時功能
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
// 這裏:調用的定時發送—— 當前時間 + 延時時間
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
2.1.3. 定時發送:sendMessageAtTime()
消息在這裏進行入隊列操作,強調一點,真正的入隊是由MessageQueue操作的。
public boolean sendMessageAtTime(@NonNull 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);
}
2.2 post方式
我相信大家在開發過程中,使用post() 的情況可能會更多一些吧。post 方式我們在使用過程上基本上都是與Runnable打交道,並未見到Message的身影。
難道Handler的消息機制不適用於post方式?今天我們去一探究竟。
先看一下使用最多的post()方法吧。
2.2.1. post()方法
public final boolean post(@NonNull Runnable r) {
// 這裏有個getPostMessage()方法
return sendMessageDelayed(getPostMessage(r), 0);
}
2.2.2. getPostMessage()
該方法給我們返回Message,將我們的Runnable賦值給Message的callback屬性。關於Message的更多內容,可以查看 Handler的前世今生3——Message
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
通過getPostMessage() 返回對應的Message對象,無縫對接 send方式。本質最後都是通過**sendMessageAtTime()**將消息入隊。
2.3 消息的入隊操作
強調一下:入隊操作的主角是MessageQueue
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
// 標示一下,這是我的消息
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
// 這裏纔是真正的入隊噢...
return queue.enqueueMessage(msg, uptimeMillis);
}
2.4 消息的複用obtain()
可以看到,消息的複用本質上還是Message來做的,這裏僅僅是做了一層封裝,畢竟Handler纔是Message的使用者。
public final Message obtainMessage()
{
return Message.obtain(this);
}
3. Handler 處理消息
我們都知道Handler是自產自銷,接下來我們就來討論一下Handler 是如何處理消息的。
我們知道在Looper的loop()方法中,handler調用了其dispatchMessage()
/**
* 最常用的方式
*/
public interface Callback {
/**
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
boolean handleMessage(@NonNull Message msg);
}
/**
* 繼承Handler時重寫該方法
* Subclasses must implement this to receive messages.
*/
public void handleMessage(@NonNull Message msg) {
}
/**
* Handle system messages here.
*/
public void dispatchMessage(@NonNull Message msg) {
// post方式,則最終還是在post的runnable中執行
if (msg.callback != null) {
handleCallback(msg);
} else {
// send方式,創建Handler中是否傳入Callback,對應進行處理
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
// 妙啊...
message.callback.run();
}
可以看到消息處理操作是在Handler中進行的。消息出隊列是在Looper中的loop()中進行的。
完美演繹啊,不得不感慨一下,這個設計真是太棒啦…
4. Handler的清理操作
在實際開發中,我們一般都會在Activity要銷燬時,調用removeCallbacksAndMessages() 方法。
Remove any pending posts of callbacks and sent messages
其實就是MessageQueue清除post的回調和send的消息。
public final void removeCallbacksAndMessages(@Nullable Object token) {
mQueue.removeCallbacksAndMessages(this, token);
}
5. 總結
- Handler的發送消息(post,send)最終都是sendMessageAtTime() 實現。
- post()是根據getPostMessage(),將Runnable 和 Message 進行綁定的。
- 消息的入隊操作都是靠enqueueMessage()
- 在dispatchMessage() 分情況處理消息。
到這裏,其實我們會發現,關於Message的重要屬性 taget , callback ,mAsynchronous 都已經在Handler中完成賦值,唯有 when屬性 沒有在Handler進行處理,奇哉怪哉…
希望大家能繼續查看:Handler的前世今生5 —— MessageQueue