Handler
的原理分析這個標題,不少文章都寫過,最近認真將源碼逐行一字一句研究,特此也簡單總結一遍。java
首先是Handler
整個Android消息機制的簡單歸納:android
分三部分對消息機制的整個流程進行闡述:git
Handler
的建立,包括Looper
、MessageQueue
的建立;Handler
發送消息,Message
是如何進入消息隊列MessageQueue
的(入列);Looper
輪詢消息,Message
出列,Handler
處理消息。// 最簡單的建立方式
public Handler() {
this(null, false);
}
// ....還有不少種方式,但這些方式最終都執行這個構造方法
public Handler(Callback callback, boolean async) {
// 1.檢查內存泄漏
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());
}
}
// 2.經過Looper.myLooper()獲取當前線程的Looper對象
mLooper = Looper.myLooper();
// 3.若是Looper爲空,拋出異常
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
的內存泄漏是一個很是常見的面試題,其實Handler
的源碼中已經將答案很是清晰告知給了開發者,即讓Handler
的導出類保證爲static
的,若是須要,將Context
做爲弱引用的依賴注入進來。github
同時,在Handler
建立的同時,會嘗試獲取當前線程惟一的Looper
對象:面試
public final class Looper {
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
}
複製代碼
關於ThreadLocal
,我在上一篇文章中已經進行了分析,如今咱們知道了ThreadLocal
保證了當前線程內有且僅有惟一的一個Looper
。數據結構
那就是須要調用Looper.prepare()
方法:app
public final class Looper {
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));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
}
複製代碼
這也就說明了,爲何當前線程沒有Looper
的實例時,會拋出一個異常並提示開發者須要調用Looper.prepare()
方法了。async
也正如上述代碼片斷所描述的,若是當前線程已經有了Looper
的實例,也會拋出一個異常,提示用戶每一個線程只能有一個Looper
(throw new RuntimeException("Only one Looper may be created per thread");
)。ide
此外,在Looper
實例化的同時,也建立了對應的MessageQueue
,這也就說明,一個線程有且僅有一個Looper
,也僅有一個MessageQueue
。oop
sendMessage()
流程以下:
// 1.發送即時消息
public final boolean sendMessage(Message msg) {
return sendMessageDelayed(msg, 0);
}
// 2.其實是發射一個延時爲0的Message
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
// 3.將消息和延時的時間進行入列(消息隊列)
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);
}
// 4.內部實際上仍是執行了MessageQueue的enqueueMessage()方法
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
複製代碼
注意第四步實際上將Handler
對象最爲target,附着在了Message
之上;接下來看MessageQueue
類內部是如何對Message
進行入列的。
boolean enqueueMessage(Message msg, long when) {
//... 省略部分代碼
synchronized (this) {
msg.markInUse();
msg.when = when;
// 得到鏈表頭的Message
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// 如有如下情景之一,將Message置於鏈表頭
// 1.頭部Message爲空,鏈表爲空
// 2.消息爲即時Message
// 3.頭部Message的時間戳大於最新Message的時間戳
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// 反之,將Message插入到鏈表對應的位置
Message prev;
// for循環就是找到合適的位置,並將最新的Message插入鏈表
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
複製代碼
MessageQueue
的數據結構自己是一個單向鏈表。
當Handler
建立好後,若在此以前調用了Looper.prepare()
初始化Looper
,還須要調用Looper.loop()
開始該線程內的消息輪詢。
public static void loop() {
// ...省略部分代碼
// 1. 獲取Looper對象
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
// 2.獲取messageQueue
final MessageQueue queue = me.mQueue;
// 3. 輪詢消息,這裏是一個死循環
for (;;) {
// 4.從消息隊列中取出消息,若消息隊列爲空,則阻塞線程
Message msg = queue.next();
if (msg == null) {
return;
}
// 5.派發消息到對應的Handler
msg.target.dispatchMessage(msg);
// ...
}
}
複製代碼
比較簡單,須要注意的一點是MessageQueue.next()
是一個可能會阻塞線程的方法,當有消息時會輪詢處理消息,但若是消息隊列中沒有消息,則會阻塞線程。
private native void nativePollOnce(long ptr, int timeoutMillis);
Message next() {
// ...省略部分代碼
int nextPollTimeoutMillis = 0;
for (;;) {
// ...
// native方法
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
// 從消息隊列中取出消息
if (msg != null) {
// 當時間小於message的時間戳時,獲取時間差
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;
}
}
// ...
}
}
複製代碼
注意代碼片斷最上方的native方法——循環體內首先調用nativePollOnce(ptr, nextPollTimeoutMillis)
,這是一個native方法,實際做用就是經過Native層的MessageQueue
阻塞nextPollTimeoutMillis
毫秒的時間:
搞清楚這一點,其它就都好理解了。
正如上文所說的,msg.target.dispatchMessage(msg)
實際上就是調用Handler.dispatchMessage(msg)
,內部最終也是執行了Handler.handleMessage()
回調:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
// 若是消息沒有定義callBack,或者不是經過
// Handler(Callback)的方式實例化Handler,
// 最終會走到這裏
handleMessage(msg);
}
}
複製代碼
Hello,我是卻把清梅嗅,若是您以爲文章對您有價值,歡迎 ❤️,也歡迎關注個人博客或者Github。
若是您以爲文章還差了那麼點東西,也請經過關注督促我寫出更好的文章——萬一哪天我進步了呢?