Handler 源码解析(Java 层)
本文由船員 ChangeHui? 自薦,轉(zhuǎn)載發(fā)布
從很早開始就認(rèn)識(shí)到 Handler 了,只不過那時(shí)修為尚淺,了解的不夠深刻,也沒有應(yīng)用自如。不過隨著工作時(shí)間的增長(zhǎng),對(duì) Handler 又有了更深層次的認(rèn)識(shí),于是有了這篇博客,希望盡可能的總結(jié)出多的知識(shí)點(diǎn)。?
Handler 在 Java 層源碼主要有 4 個(gè)類:Looper、MessageQueue、Message、Handler。我歸納了他們的幾個(gè)主要知識(shí)點(diǎn):?
Looper:sThreadLocal、Looper.loop();
Message:數(shù)據(jù)結(jié)構(gòu)、消息緩存池;
MessageQueue:enqueueMessage、next、管道等待、同步消息隔離、idleHandler;
Handler:send/post、dispatchMessage 消息處理優(yōu)先級(jí)。
Looper
Looper 數(shù)據(jù)結(jié)構(gòu)
static?final?ThreadLocal<Looper>?sThreadLocal?=?new?ThreadLocal<Looper>();private?static?Looper?sMainLooper;??//?guarded?by?Looper.class
final?MessageQueue?mQueue;
//?sThreadLocal
private?static?void?prepare(boolean?quitAllowed)?{
????if?(sThreadLocal.get()?!=?null)?{?throw?Exception?...?}
????sThreadLocal.set(new?Looper(quitAllowed));
}
public?static?@Nullable?Looper?myLooper()?{
????return?sThreadLocal.get();
}
//?sMainLooper
public?static?void?prepareMainLooper()?{
????prepare(false);
????synchronized?(Looper.class)?{
????????if?(sMainLooper?!=?null)?{?throw?Exception?...}
????????sMainLooper?=?myLooper();
????}
}
public?static?Looper?getMainLooper()?{
????synchronized?(Looper.class)?{
????????return?sMainLooper;
????}
}
//?mQueue
private?Looper(boolean?quitAllowed)?{
????mQueue?=?new?MessageQueue(quitAllowed);
????mThread?=?Thread.currentThread();
}
public?static?@NonNull?MessageQueue?myQueue()?{
????return?myLooper().mQueue;
}
sThreadLocal:靜態(tài)常量,保證一個(gè)線程只有一個(gè) Looper;
sMainLooper:靜態(tài)變量,在 prepareMainLooper 中賦值當(dāng)前線程 Looper;
mQueue:變量,Looper 構(gòu)造函數(shù)中初始化,因?yàn)橐粋€(gè)線程只有一個(gè) Looper,所以也同樣只有一個(gè) mQueue。
通過以上分析,我們可以總結(jié)出一下特性:?
Looper、MessageQueue 是線程唯一的;
一個(gè)進(jìn)程只有一個(gè) sMainLooper;
根據(jù) ThreadLocal 的特性,可通過 myLooper 方法獲取當(dāng)前線程的 Looper。
Looper.loop()
public?static?void?loop()?{????final?Looper?me?=?myLooper();
????final?MessageQueue?queue?=?me.mQueue;
????for?(;;)?{?
????????Message?msg?=?queue.next();
????????...
????????msg.target.dispatchMessage(msg);
????????...
????????msg.recycleUnchecked();
????}
}
Looper.loop() 方法雖然看起來很多,其實(shí)他主要就做了三件事:?
從消息隊(duì)列中獲取下一個(gè)消息;
msg.target 就是 handler,通過 dispatchMessage 方法把消息分發(fā)下去,這個(gè)方法下面會(huì)有說到;
消息回收,放到消息緩存池里。這里需要注意的是 Message 對(duì)象并沒有釋放,會(huì)緩存起來。
Message
Message 數(shù)據(jù)結(jié)構(gòu)
public?int?what,?arg1,?arg2;public?Object?obj;
public?Messenger?replyTo;
int?flags;
long?when;??????//?消息發(fā)送時(shí)間
Bundle?data;
Handler?target;
Runnable?callback;
Message?next;
private?static?final?Object?sPoolSync?=?new?Object();
private?static?Message?sPool;
private?static?int?sPoolSize?=?0;
private?static?final?int?MAX_POOL_SIZE?=?50;
看到 next 變量,我們會(huì)想到單鏈表,其實(shí) Message 就相當(dāng)于單鏈表的 node,MessageQueue 就是一個(gè)單鏈表了,會(huì)持有表頭的引用;
what、arg1、arg2、obj、data 就是我們發(fā)送的一些信息;
值得注意的是 target,他是 Handler 類型,就是本消息的 Handler,會(huì)在 Handler 發(fā)送消息的時(shí)候賦值;
后面的四個(gè)對(duì)象,都是和消息緩存池有關(guān)的。
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();
}
void?recycleUnchecked()?{
????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)?{
????????????next?=?sPool;
????????????sPool?=?this;
????????????sPoolSize++;
????????}
????}
}
事實(shí)上緩存池的數(shù)據(jù)結(jié)構(gòu)也是一個(gè)鏈表,sPool 為鏈表頭引用,最大容量為 50;
回收消息時(shí),會(huì)把消息里面所有參數(shù)重置,并把當(dāng)前消息設(shè)為鏈表頭;
獲取消息時(shí),返回當(dāng)前鏈表頭,并把 next 置空。
MessageQueue
插入隊(duì)列
boolean?enqueueMessage(Message?msg,?long?when)?{????synchronized?(this)?{
????????msg.markInUse();
????????msg.when?=?when;
????????Message?p?=?mMessages;
????????boolean?needWake;
????????if?(p?==?null?||?when?==?0?||?when?<?p.when)?{
????????????//?作為表頭,如果隊(duì)列是阻塞狀態(tài)則需要喚醒
????????????msg.next?=?p;
????????????mMessages?=?msg;
????????????needWake?=?mBlocked;
????????}?else?{
????????????//?根據(jù)時(shí)間順序,插入鏈表中間
????????????needWake?=?mBlocked?&&?p.target?==?null?&&?msg.isAsynchronous();
????????????Message?prev;
????????????for?(;;)?{
????????????????prev?=?p;
????????????????p?=?p.next;
????????????????if?(p?==?null?||?when?<?p.when)?{
????????????????????break;
????????????????}
????????????????if?(needWake?&&?p.isAsynchronous())?{
????????????????????needWake?=?false;
????????????????}
????????????}
????????????msg.next?=?p;?//?插入消息
????????????prev.next?=?msg;
????????}
????????//?We?can?assume?mPtr?!=?0?because?mQuitting?is?false.
????????if?(needWake)?{
????????????nativeWake(mPtr);
????????}
????}
????return?true;
}
主要作為插入隊(duì)列的方法,有下列幾個(gè)特性:?
把消息加入消息隊(duì)列,如果當(dāng)前表頭為空,則把消息作為表頭引用;如果不為空,則會(huì)根據(jù)時(shí)間的順序,插入到對(duì)應(yīng)的時(shí)間中;
nativeWake 是調(diào)用底層在管道中寫操作以喚醒,在隊(duì)列不是阻塞的狀態(tài)下是不需要喚醒的;
另外注意其中用了 synchronized 關(guān)鍵字,說明消息隊(duì)列的插入是線性安全的,刪除也是線性安全的,之后我們會(huì)說到。
MessageQueue.next()
for?(;;)?{????nativePollOnce(ptr,?nextPollTimeoutMillis);
????synchronized?(this)?{
????????final?long?now?=?SystemClock.uptimeMillis();
????????Message?prevMsg?=?null;
????????Message?msg?=?mMessages;
????????if?(msg?!=?null?&&?msg.target?==?null)?{
????????????//?如果有同步消息隔離,則會(huì)優(yōu)先查找異步消息
????????????do?{
????????????????prevMsg?=?msg;
????????????????msg?=?msg.next;
????????????}?while?(msg?!=?null?&&?!msg.isAsynchronous());
????????}
????????if?(msg?!=?null)?{
????????????if?(now?<?msg.when)?{
????????????????//?計(jì)算距離下一個(gè)消息的時(shí)間
????????????????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?{
????????????//?沒有更多消息的時(shí)候,nextPollTimeoutMillis?會(huì)置為?1。
????????????nextPollTimeoutMillis?=?-1;
????????}
????????...
????}
????//?如果目前沒有消息,已經(jīng)處在空閑狀態(tài),則執(zhí)行?idler.queueIdle
????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);
????????????}
????????}
????}
????...
}
此方法會(huì)從消息隊(duì)列中讀取下一個(gè)消息返回,主要做了以下操作:?
nativePollOnce 函數(shù)會(huì)調(diào)用底層管道操作函數(shù),nextPollTimeoutMillis 為 -1 時(shí),會(huì)阻塞,為 0 時(shí)不會(huì)阻塞,大于 0 時(shí),會(huì)阻塞相應(yīng)的時(shí)間;
如果有同步消息隔離,則會(huì)優(yōu)先查找異步消息;
獲取當(dāng)前時(shí)間隊(duì)列的消息,并返回;
如果隊(duì)列沒有任何消息,則會(huì)執(zhí)行 idler.queueIdle,通知監(jiān)聽者當(dāng)前隊(duì)列處于空閑狀態(tài)。
同步消息隔離
上面我們有提到了同步消息隔離,這里我們介紹一下。同步隔離,有時(shí)候也可以叫異步消息,說的是一個(gè)意思。在源碼中主要用于優(yōu)先更新 UI。
private?IdleHandler[]?mPendingIdleHandlers;public?int?postSyncBarrier()?{
????return?postSyncBarrier(SystemClock.uptimeMillis());
}
private?int?postSyncBarrier(long?when)?{
????//?向消息隊(duì)列中加入一個(gè)?handler?為空的消息
????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;
????}
}
如上 postSyncBarrier 函數(shù)中會(huì)向消息隊(duì)列中加入一個(gè) handler(即 Message 的 target) 為空的消息作為標(biāo)識(shí)。在我們上面 MessageQueue.next() 的函數(shù)中,當(dāng) msg.target == null 時(shí),會(huì)優(yōu)先獲取異步消息并返回。??
因此想要使用異步消息有兩個(gè)條件:
消息為異步消息,即 msg.isAsynchronous() 返回 false;
需要獲取當(dāng)前隊(duì)列并運(yùn)行 postSyncBarrier() 函數(shù)。
IdleHandler
Handler 還提供了消息隊(duì)列空閑狀態(tài)通知。
private?final?ArrayList<IdleHandler>?mIdleHandlers?=?new?ArrayList<IdleHandler>();public?void?addIdleHandler(@NonNull?IdleHandler?handler)?{
????if?(handler?==?null)?{
????????throw?new?NullPointerException("Can't?add?a?null?IdleHandler");
????}
????synchronized?(this)?{
????????mIdleHandlers.add(handler);
????}
}
public?void?removeIdleHandler(@NonNull?IdleHandler?handler)?{
????synchronized?(this)?{
????????mIdleHandlers.remove(handler);
????}
}
IdleHandler 的源碼比較簡(jiǎn)單,就是一個(gè) ArrayList,然后進(jìn)行增加刪除操作。注意,這個(gè)也是線性安全的。
Handler
post/sendMessage
public?final?boolean?post(Runnable?r){????return?sendMessageDelayed(getPostMessage(r),?0);
}
public?final?boolean?sendMessage(Message?msg){
????return?sendMessageDelayed(msg,?0);
}
private?static?Message?getPostMessage(Runnable?r)?{
????Message?m?=?Message.obtain();
????m.callback?=?r;
????return?m;
}
private?boolean?enqueueMessage(MessageQueue?queue,?Message?msg,?long?uptimeMillis)?{
????msg.target?=?this;
????if?(mAsynchronous)?{
????????msg.setAsynchronous(true);
????}
????return?queue.enqueueMessage(msg,?uptimeMillis);
}
sendMessage 和 post 最本質(zhì)的區(qū)別是之后處理任務(wù)時(shí)的優(yōu)先級(jí),post 會(huì)處理 Runnable 中的任務(wù),而 sendMessage 會(huì)回調(diào)給 handler 處理;
他們最終都會(huì)走 enqueueMessage 方法,并設(shè)置當(dāng)前 Handler 為 msg.target。
dispatchMessage
public?void?dispatchMessage(Message?msg)?{????if?(msg.callback?!=?null)?{
????????handleCallback(msg);
????}?else?{
????????if?(mCallback?!=?null)?{
????????????if?(mCallback.handleMessage(msg))?{
????????????????return;
????????????}
????????}
????????handleMessage(msg);
????}
}
任務(wù)執(zhí)行時(shí)就會(huì)運(yùn)行這個(gè)函數(shù),主要是一個(gè)優(yōu)先級(jí)的問題:
callback 優(yōu)先級(jí)最高,也就是 post 發(fā)送的消息
mCallback.handleMessage(msg),優(yōu)先級(jí)第二
handleMessage(msg),優(yōu)先級(jí)第三
(原文完)
提前祝大家周末愉快,下周見。
轉(zhuǎn)載于:https://blog.51cto.com/13360987/2386015
總結(jié)
以上是生活随笔為你收集整理的Handler 源码解析(Java 层)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux环境下vim创建java文件,
- 下一篇: 学习oop知识之OOP的封装