- 前言
- Handler 流程
- 流程源碼
-
面試題
- Message 創(chuàng)建回收 鏈表關(guān)系
- MessageQueue 創(chuàng)建的時(shí)間
- ThreadLocal在Handler機(jī)制中的作用
- Looper,Handler,MessageQueue的引用關(guān)系?
- Handler post 和 sendMessage 區(qū)別
- Handler導(dǎo)致內(nèi)存泄露問(wèn)題?
- 有了解過(guò) Handler的同步屏障機(jī)制么恕酸?
- 主線程Looper.loop 為何不會(huì)卡死
- Message 復(fù)用機(jī)制
- MessageQueue.removeMessages中的操作
- Handler.post
本博客為本人學(xué)習(xí) Handler 中的筆記,可能文筆方面欠妥搂捧,該文中本人會(huì)描述主要源碼和部分經(jīng)典面試題答案的源碼解讀认然。閱讀時(shí)間:20分鐘+ 本文源碼版本 android SDK 28
update time : 2019年12月30日19:01:22
v1.2 : 添加面試題(message復(fù)用 和 Handler.post 的流程)
v1.3 : 修改個(gè)人本身對(duì) MessageQueue 理解錯(cuò)誤,重點(diǎn)修改 Message 單鏈表對(duì)應(yīng)的知識(shí)點(diǎn)
前言
關(guān)于Google 建議在主線程中更新UI (其實(shí)子線程也可以更新UI,但是不推薦)多線程同步更新UI 科侈,容易使UI進(jìn)入不可預(yù)測(cè)的狀態(tài)四苇。
將工作線程中需更新UI的操作信息 傳遞到 UI主線程装处,從而實(shí)現(xiàn) 工作線程對(duì)UI的更新繪制等處理闭翩,最終實(shí)現(xiàn)異步消息的處理挣郭。(保證多線程安全 數(shù)據(jù)更新的順序性)
Handler 流程
上圖已經(jīng)比較清晰地講述了整個(gè)handler 的過(guò)程其中牽扯到比較重要的 類有:
- 處理器 類(Handler)
- 消息隊(duì)列 類(MessageQueue)
- 循環(huán)器 類(Looper)
三個(gè)對(duì)象直接的關(guān)系
Handler -> Handler.sendMessage() / post() -> MessageQueue -> MessageQueue.next() -> Looper -> Looper.prepare() -> Looper.loop() -> handlerMessage() 回到Handler中。
關(guān)于MessageQueue先說(shuō)明一點(diǎn)疗韵,該隊(duì)列的實(shí)現(xiàn)既非Collection的子類兑障,亦非Map的子類,而是Message本身蕉汪。因?yàn)镸essage本身就是鏈表節(jié)點(diǎn)流译。
流程源碼
這里主要講述三個(gè)對(duì)象整個(gè)流程的源碼分析,順帶 說(shuō)一些面試題的點(diǎn)
Handler 初始化
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());
}
}
// 內(nèi)部通過(guò) ThreadLocal<Looper>.get() 獲取的 唯一的loop對(duì)象
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
// 獲取該Looper對(duì)象中保存的消息隊(duì)列對(duì)象(MessageQueue)
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
上述代碼為Handler 中的構(gòu)造方法部分,mLooper = Looper.myLooper() 通過(guò)ThreadLocal<Looper>.get() 獲取到 Loop對(duì)象中 的Loop存儲(chǔ)對(duì)象者疤,這里也就保證了 一個(gè)線程可以有多個(gè)Handler 福澡,一個(gè)Handler 只能綁定一個(gè) Looper。有g(shù)et()方法了我們就找一下 在那個(gè)地方set進(jìn)去的宛渐。接下來(lái)我們進(jìn)入Looper 源碼部分。
Looper 初始化
// quitAllowed 參數(shù)為 MessageQueue 是否可以quit
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));
}
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
通過(guò)上述代碼 在 prepare() 中為sThreadLocal 傳入新的Looper 對(duì)象眯搭,Looper()中創(chuàng)建了 messageQueue 窥翩,獲取當(dāng)前線程的 對(duì)象。
所以 一個(gè)線程可以有多個(gè)Handler 鳞仙,一個(gè)Handler 只能綁定一個(gè) Looper 寇蚊,一個(gè)Looper 中有一個(gè)MessageQueus (好他瞄的繞 但是面試官真的這樣問(wèn)過(guò)我),通過(guò)反推 我們可以 給面試官講述 Handler Looper MessageQueus 之間的 創(chuàng)建聲明關(guān)系棍好。
prepareMainLooper()對(duì)應(yīng)著 創(chuàng)建主線程時(shí)仗岸,會(huì)自動(dòng)調(diào)用ActivityThread的1個(gè)靜態(tài)的main();而main()內(nèi)則會(huì)調(diào)用Looper.prepareMainLooper()為主線程生成1個(gè)Looper對(duì)象借笙,同時(shí)也會(huì)生成其對(duì)應(yīng)的MessageQueue對(duì)象扒怖,這也就是我們?yōu)楹尾挥迷?OnCreate 的時(shí)候 設(shè)置Looper.prepare() 。
Handler 發(fā)送消息
sendMessage() / postMessage() 發(fā)送消息 业稼,post() 則是 實(shí)現(xiàn)一個(gè) Runable 不需要外部創(chuàng)建消息對(duì)象盗痒。
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
// 跳轉(zhuǎn)到sendMessageAtTime
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
// 跳轉(zhuǎn)到 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);
}
核心代碼 就可以看到 queue.enqueueMessage() 方法,我們來(lái)看一下源碼
// enqueueMessage核心辦法
Message p = mMessages;
if (p == null || when == 0 || when < p.when) {
// 如果Message隊(duì)列為空的 則將信息添加到頭部
msg.next = p;
mMessages = msg;
} else {
...
Message prev;
for (;;) {
// 通過(guò) prev = p低散、 p.next 遞歸鏈表 比對(duì)when
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
...
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
Message 通過(guò) next 來(lái)將Message 本身當(dāng)做節(jié)點(diǎn) 拼接為單鏈表俯邓,when 代表的就是 Message 觸發(fā)的時(shí)間骡楼,所以循環(huán) 比對(duì)時(shí)間 如果觸發(fā) when < p.when,則退出循環(huán) 進(jìn)行插入 p 的操作稽鞭。
Looper.loop -> MessageQueue.next
消息循環(huán)鸟整,即從消息隊(duì)列中獲取消息、分發(fā)消息到Handler 朦蕴。下為部分核心源碼
public static void loop() {
// 1. 獲取當(dāng)前Looper的消息隊(duì)列
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
// myLooper()作用:返回sThreadLocal存儲(chǔ)的Looper實(shí)例
final MessageQueue queue = me.mQueue;
for (;;) {
// 從消息隊(duì)列中取出消息
Message msg = queue.next();
if (msg == null) {
return;
}
// next():取出消息隊(duì)列里的消息
// 若取出的消息為空篮条,則線程阻塞
// 派發(fā)消息到對(duì)應(yīng)的Handler
msg.target.dispatchMessage(msg);
// 把消息Message派發(fā)給消息對(duì)象msg的target屬性
// target屬性實(shí)際是1個(gè)handler對(duì)象
msg.recycleUnchecked();
}
}
通過(guò) Looper 獲取到 MessageQueue ,然后從 queue 中 取出消息梦重,然后回調(diào) msg中的 target 走dispatchMessage方法 回調(diào) 到我們的主線程的Handler 的 handlermessage () 方法中兑燥,然后 在調(diào)用recycleUnchecked方法將 msg 標(biāo)識(shí)和 next 等標(biāo)記設(shè)置為初始值。
下邊我們進(jìn)入 MessageQueue.next() / dispatchMessage(msg) 看看源碼
Message next() {
// 該參數(shù)用于確定消息隊(duì)列中是否還有消息
// 從而決定消息隊(duì)列應(yīng)處于出隊(duì)消息狀態(tài) / 等待狀態(tài)
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// nativePollOnce方法在native層琴拧,若是nextPollTimeoutMillis為-1降瞳,此時(shí)消息隊(duì)列處于等待狀態(tài)
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
// 出隊(duì)消息,從消息隊(duì)列中取出消息:按創(chuàng)建Message對(duì)象的時(shí)間順序
if (msg != null) {
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;
}
} else {
// 若 消息隊(duì)列中已無(wú)消息蚓胸,則將nextPollTimeoutMillis參數(shù)設(shè)為-1
// 下次循環(huán)時(shí)挣饥,消息隊(duì)列則處于等待狀態(tài)
nextPollTimeoutMillis = -1;
}
......
}
.....
}
}
上述代碼主要功能都有注釋 表明了,通過(guò) Looper.loop 到 msgQueue.next 循環(huán) 隊(duì)列沛膳,獲取有用的消息進(jìn)行出列操作扔枫,沒(méi)有消息就更新標(biāo)識(shí),阻塞循環(huán)锹安。
上文的 msg.target.dispatchMessage(msg); 其實(shí)target 就是Handler 對(duì)象 我們通過(guò)跟蹤找到 dispatchMessage 源碼
Handler 消息處理源碼
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
public interface Callback {
/**
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
public boolean handleMessage(Message msg);
}
// 我們必須實(shí)現(xiàn)的 子類方法
public void handleMessage(Message msg) {
}
在進(jìn)行消息分發(fā)時(shí)dispatchMessage(msg)短荐,會(huì)進(jìn)行1次發(fā)送方式的判斷:
若msg.callback屬性不為空,則代表使用了post(Runnable r)發(fā)送消息叹哭,則直接回調(diào)Runnable對(duì)象里復(fù)寫(xiě)的run()
若msg.callback屬性為空忍宋,則代表使用了sendMessage(Message msg)發(fā)送消息,則回調(diào)復(fù)寫(xiě)的handleMessage(msg)
面試題
Message 創(chuàng)建回收 鏈表關(guān)系
這里分享一個(gè) 有圖 有源碼 自我感覺(jué)不錯(cuò)的博客
Message中obtain()與recycle()的來(lái)龍去脈
MessageQueue 創(chuàng)建的時(shí)間
上文Looper 在創(chuàng)建的時(shí)候有源碼 messageQueue 是和Looper 一塊創(chuàng)建的
ThreadLocal在Handler機(jī)制中的作用
sThreadLocal是一個(gè)ThreadLocal對(duì)象风罩,可以在一個(gè)線程中存儲(chǔ)變量糠排。上文 Looper.prepare() 源碼出可以看到 從中g(shù)et到一個(gè) Looper 對(duì)象,sThreadLocal存儲(chǔ)的就是Looper 對(duì)象超升。也就說(shuō)明了 一個(gè)handler 對(duì)應(yīng)一個(gè)Looper 入宦。
Looper,Handler,MessageQueue的引用關(guān)系?
這里直接 引用上文 寫(xiě)的:“ Handler -> Handler.sendMessage() / post() -> MessageQueue -> MessageQueue.next() -> Looper -> Looper.prepare() -> Looper.loop() -> handlerMessage() 回到Handler中“ 。
Handler post 和 sendMessage 區(qū)別
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
查看post源碼 里邊其實(shí)還是走的SendMessage 室琢,不過(guò) post方法對(duì) msg的callback和target都有賦值乾闰,上文提到的 Handler.dispatchMessage 方法中有提到 對(duì)這兩種方法有一個(gè)區(qū)分 反饋處理。
Handler導(dǎo)致內(nèi)存泄露問(wèn)題?
這個(gè)問(wèn)題在 三年工作經(jīng)驗(yàn)以后穩(wěn)得比較少 盈滴,主要考察 生命周期 和gc 的問(wèn)題汹忠。因?yàn)?匿名內(nèi)部類 Handler 中包含 外部對(duì)象 也就是Activity 對(duì)象,如果界面銷毀但是 后邊的Queue還在 消息沒(méi)有處理,就有可能造成 內(nèi)存泄漏宽菜。
簡(jiǎn)單的處理方法:
static class MyHandler extends Handler {
// 弱引用 Avtivity
WeakReference mActivityReference;
// 如果界面銷毀 handler 交給 gc自己清理
MyHandler(Activity activity) {
mActivityReference= new WeakReference(activity);
}
@Override
public void handleMessage(Message msg) {
final Activity activity = mActivityReference.get();
if (activity != null) {
mImageView.setImageBitmap(mBitmap);
}
}
}
有了解過(guò) Handler的同步屏障機(jī)制么谣膳?
這一點(diǎn)說(shuō)實(shí)話有點(diǎn)冷門(mén) 但是也可以看出你對(duì)源碼的解讀是否深刻,(我也是從朋友 強(qiáng)哥哪里聽(tīng)來(lái)的 360大佬牛逼)
在說(shuō) 同步屏障的前提铅乡,我們需要先說(shuō) Message 可以添加 同步消息和 消息
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
上述代碼的 async 布爾值 便是同步和異步的標(biāo)識(shí)
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
在加入 Message 的時(shí)候 調(diào)用msg.setAsynchronous(true)
同步屏障就是為了 區(qū)分 MessageQueue 中的message 的優(yōu)先級(jí)的继谚。通過(guò)打開(kāi) 同步屏障 可以在 looper 循環(huán)的時(shí)候使 異步消息優(yōu)先處理并返回。我們通過(guò)源碼看看:
private int postSyncBarrier(long when) {
// Enqueue a new sync barrier token.
// We don't need to wake the queue because the purpose of a barrier is to stall it.
synchronized (this) {
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
Message prev = null;
Message p = mMessages;
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}
注意這里 Message 的tager 其實(shí)為null ,我們重新貼一下 Looper.loop () 看下 源碼怎么處理的
Message next() {
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
從上面的代碼中阵幸,我們知道花履,當(dāng)MessageQueue取到一個(gè)target為null的Message是,會(huì)先執(zhí)行異步消息挚赊,已達(dá)到 后添加進(jìn)去的消息诡壁,先處理通過(guò) 所謂的同步屏障 達(dá)到修改鏈表順序處理的機(jī)制。
主線程Looper.loop 為何不會(huì)卡死
由于這個(gè)問(wèn)題 其實(shí)我自己當(dāng)時(shí)也不是很明白 給出 參考文章
這個(gè)問(wèn)題 涵蓋面就有點(diǎn)多了荠割,不光要說(shuō) Handler 還有說(shuō) linux epoll機(jī)制.
為了可以讓App一直運(yùn)行 妹卿,我們是絕不希望會(huì)被運(yùn)行一段時(shí)間,自己就退出蔑鹦,那么如何保證能一直存活呢夺克?簡(jiǎn)單做法就是可執(zhí)行代碼是能一直執(zhí)行下去的,死循環(huán)便能保證不會(huì)被退出嚎朽,例如铺纽,binder線程也是采用死循環(huán)的方法,通過(guò)循環(huán)方式不同與Binder驅(qū)動(dòng)進(jìn)行讀寫(xiě)操作哟忍,當(dāng)然并非簡(jiǎn)單地死循環(huán)狡门,無(wú)消息時(shí)會(huì)休眠。
這里就涉及到Linux pipe/epoll機(jī)制锅很,簡(jiǎn)單說(shuō)就是在主線程的MessageQueue沒(méi)有消息時(shí)其馏,便阻塞在loop的queue.next()中的nativePollOnce()方法里,詳情見(jiàn)Android消息機(jī)制1-Handler(Java層)粗蔚,此時(shí)主線程會(huì)釋放CPU資源進(jìn)入休眠狀態(tài)尝偎,直到下個(gè)消息到達(dá)或者有事務(wù)發(fā)生饶火,通過(guò)往pipe管道寫(xiě)端寫(xiě)入數(shù)據(jù)來(lái)喚醒主線程工作鹏控。這里采用的epoll機(jī)制,是一種IO多路復(fù)用機(jī)制肤寝,可以同時(shí)監(jiān)控多個(gè)描述符当辐,當(dāng)某個(gè)描述符就緒(讀或?qū)懢途w),則立刻通知相應(yīng)程序進(jìn)行讀或?qū)懖僮骼鹂矗举|(zhì)同步I/O缘揪,即讀寫(xiě)是阻塞的。 所以說(shuō),主線程大多數(shù)時(shí)候都是處于休眠狀態(tài)找筝,并不會(huì)消耗大量CPU資源蹈垢。
關(guān)于更加詳細(xì)的 和本片文章Handler 沾邊比較少的 ,則可以異步鏈接地址觀看 Git yuan大佬的詳細(xì)解讀袖裕。
Message 復(fù)用機(jī)制
在Loop.looper 中我們 就可以初探原因曹抬。
// Loop 類
for (;;) {
Message msg = queue.next(); // might block
...
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
...
try {
// 消息分發(fā) target 其實(shí)指的 Handler (這里也是 Handler sendMsg 和 post 方法的區(qū)別所在)
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
...
final long newIdent = Binder.clearCallingIdentity();
// 回收方法 我們重點(diǎn)看一下這個(gè)方法
msg.recycleUnchecked();
}
// Message 類
void recycleUnchecked() {
// 改變標(biāo)記 加入消息池中 并重置所有狀態(tài)
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
// 頭節(jié)點(diǎn)設(shè)置給Next 將當(dāng)前對(duì)象最為最新的頭節(jié)點(diǎn)sPool
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
我們還要看下 關(guān)于 Message .obtain() 中如何 處理的
// 部分需要標(biāo)注的對(duì)象
public static final Object sPoolSync = new Object();
private static Message sPool;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50;
// 消息回首復(fù)用主要方法
public static Message obtain() {
synchronized (sPoolSync) {
//判斷頭節(jié)點(diǎn)是否null
if (sPool != null) {
// 取出頭結(jié)點(diǎn) 并將下一個(gè)消息設(shè)置為頭結(jié)點(diǎn)
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
// 如果消息鏈表為null 則返回新msg
return new Message();
}
Message通過(guò)靜態(tài)單鏈表來(lái)全局完成消息的復(fù)用,而在每次回收的過(guò)程中消息數(shù)據(jù)重置防止Message持有其他對(duì)象而造成內(nèi)存泄漏操作急鳄,所有在日常開(kāi)發(fā)開(kāi)發(fā)中盡量使用Mesaage.obtain 來(lái)獲取Message谤民。
MessageQueue.removeMessages中的操作
這一部分也是 朋友魚(yú)總 問(wèn)我的,答不上來(lái) 進(jìn)行了一次百度疾宏,并附上參考文章地址 :MessageQueue.removeMessages中的操作
Handler.post
Handler.sendmsg 和 Handler.post 兩種方式 不同的是 post方法只需要完成 runable 方法實(shí)現(xiàn)就好张足,他底層就做 了什么操作呢?
// handler.class
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
// 對(duì)msg的 callback 進(jìn)行賦值
m.callback = r;
return m;
}
// 在Loop.loop 中 對(duì)消息進(jìn)行的分發(fā)代碼中 對(duì)msg的callback進(jìn)行分類處理坎藐。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
// 回調(diào) post 方法中的 runable 方法
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
// 回到到 handler 的 handleMessage 方法中
handleMessage(msg);
}
}
部分講解 都在代碼的注釋中了为牍。大體handler 本人學(xué)習(xí)和部分面試題總結(jié) 大概就這些了。后續(xù)還會(huì)持續(xù)維護(hù)更新顺饮。