做Android研發(fā)的同學(xué)不管是面試還是平時(shí)的工作中一定會(huì)遇到各種Handler相關(guān)的問題赶站,這一篇我們就通過源代碼一起探討一下Android的Handler機(jī)制。本文的分析基于Android8.0源碼。
1、一個(gè)小問題,有代碼如下悬而。一個(gè)函數(shù)延遲2秒執(zhí)行,另一個(gè)函數(shù)延遲3秒執(zhí)行锭汛,其中第一個(gè)函數(shù)執(zhí)行了3秒笨奠,請(qǐng)問第二個(gè)函數(shù)會(huì)在什么時(shí)候執(zhí)行袭蝗。
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 1:
methodOne();
break;
case 2:
methodTwo();
break;
default:
break;
}
}
};
handler.sendEmptyMessageDelayed(1, 2000);
handler.sendEmptyMessageDelayed(2, 3000);
private void methodTwo() {
System.out.println("methodTwo");
}
private void methodOne() {
System.out.println("methodOne");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("methodOneFinish");
}
帶著這個(gè)問題,開始本文分析般婆。
先看構(gòu)造函數(shù)
public Handler() {
this(null, false);
}
public Handler(Looper looper) {
this(looper, null, false);
}
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
public Handler(boolean async) {
this(null, async);
}
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());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
默認(rèn)構(gòu)造函數(shù)callback為null到腥,async為false,async如果為true蔚袍,則處理程序?qū)⒄{(diào)用Message#setAsynchronous乡范,looper就是Looper.myLooper()的返回值,是從ThreadLocal中g(shù)et到的一個(gè)值,也就是如下代碼
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
跟蹤到ThreadLocal的get函數(shù)
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
第一行,獲取當(dāng)前線程茴她;第二行,通過當(dāng)前線程獲取ThreadLocalMap實(shí)例栈拖。我們跟蹤進(jìn)去看一下
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
進(jìn)入Thread類,發(fā)現(xiàn)t.threadLocals指的是ThreadLocal.ThreadLocalMap没陡,初始值為null。
所以會(huì)調(diào)用setInitialValue函數(shù)
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
value返回為null索赏,map初始值也為null盼玄,所以會(huì)進(jìn)createMap
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
這里的泛型T是ThreadLocal包裹的泛型對(duì)象,在Looper類中潜腻,ThreadLocal包裹的泛型對(duì)象就是Looper埃儿。那這段代碼的意思就是實(shí)例化了一個(gè)ThreadLocalMap,key為ThreadLocal類融涣,value為L(zhǎng)ooper對(duì)象童番。ThreadLocalMap類在此不做詳細(xì)說明,就理解為一個(gè)類似于鍵值對(duì)存儲(chǔ)的數(shù)據(jù)結(jié)構(gòu)就行威鹿。根據(jù)上面的代碼分析剃斧,這里的value為null,所以最后return的也是null忽你。
顯而易見幼东,如果myLooper返回值是這樣的話Handler就無法工作了。所以我們要提到另外一個(gè)重要的函數(shù)科雳,Looper.prepare
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));
}
這里sThreadLocal.get() 不能有值根蟹,因此,每個(gè)線程只能調(diào)用一次Looper.prepare函數(shù)糟秘。下面一行是ThreadLocal的set的過程
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
這時(shí)候value就是new的一個(gè)Looper對(duì)象了简逮,這樣就做到了以ThreadLocal為key,Looper為value的鍵值對(duì)存進(jìn)了ThreadLocalMap尿赚,ThreadLocalMap與當(dāng)前線程進(jìn)行了綁定散庶。這樣就做到了從當(dāng)前線程中獲取Looper蕉堰。
所以在使用Handler之前,必須執(zhí)行Looper.prepare督赤,之所以我們平時(shí)在主線程中new Handler不需要執(zhí)行嘁灯,是因?yàn)樵贏ctivityThread中已經(jīng)幫我們執(zhí)行了。參看ActivityThread第6525行躲舌。
Looper.prepareMainLooper();
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
到此為止丑婿,構(gòu)造函數(shù)中Looper的分析過程已完成,下面我們需要看另一個(gè)重要類的分析没卸,也就是 mQueue = mLooper.mQueue這行代碼的分析羹奉。mQuene是Handler類中的一個(gè)成員變量,類名為MessageQueue约计,根據(jù)英文翻譯诀拭,可以理解為消息隊(duì)列,但內(nèi)部的數(shù)據(jù)結(jié)構(gòu)并不是隊(duì)列煤蚌,內(nèi)部存儲(chǔ)的都是Message耕挨,從代碼層面來講其實(shí)是單鏈表的結(jié)構(gòu)。mQuene來源于mLooper的成員變量尉桩,初始化的過程在
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
Message類有幾個(gè)重要的屬性筒占,在這里先大概列出來
public int what;
public int arg1;
public int arg2;
public Object obj;
long when;
Bundle data;
Handler target;
Message next;
前面打好了基礎(chǔ),接下來蜘犁,我們就可以開始看整個(gè)Handler的運(yùn)行機(jī)制了翰苫。以文章開頭的問題為例,我們來看下代碼的執(zhí)行過程这橙。首先是new Handler奏窑,這個(gè)上文已經(jīng)分析過了,然后里面有一個(gè)handleMessage的覆蓋屈扎,我們看下這里是怎么回調(diào)過來的埃唯。
首先來到ActivityThread的第6541行
Looper.loop();
這個(gè)loop函數(shù)有50多行,我們從頭分析鹰晨。前幾行是這樣的
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
通過throw的異持欤可以看出,在線程中執(zhí)行l(wèi)oop之前必須先prepare并村。
然后拿到了MessageQuene實(shí)例巍实。
再下面幾行,啟動(dòng)了一個(gè)無限for循環(huán)
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
目的就是不斷的從quene中取message哩牍,message為null的時(shí)候退出循環(huán)棚潦。那我們需要看下next函數(shù)的具體實(shí)現(xiàn)。
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
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) {
可以看到膝昆,它也有一個(gè)無限for循環(huán)丸边。然后有一個(gè)nativePollOnce函數(shù)的調(diào)用叠必,這是一個(gè)native的函數(shù),也就是c++的實(shí)現(xiàn)妹窖。主要功能是等待纬朝,直到有下一條消息為止。至于為什么死循環(huán)不會(huì)導(dǎo)致應(yīng)用卡死(也就是ANR)骄呼,這個(gè)我們稍后再討論共苛,先繼續(xù)往下看◎烟眩可以看到synchronized關(guān)鍵字修飾了this隅茎,可以看到取消息的過程是線程安全的。定義了兩個(gè)參數(shù)prevMsg表示之前那條消息嫉沽,msg表示下一條消息辟犀。
// 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 {
下面一個(gè)do while循環(huán),從鏈表中尾部取出消息绸硕。當(dāng)now小于msg.when堂竟,也就是當(dāng)消息的執(zhí)行之間大于現(xiàn)在的時(shí)間(這個(gè)when是開發(fā)者設(shè)定的消息執(zhí)行時(shí)間點(diǎn),后面也會(huì)解釋)玻佩,下一條message還沒準(zhǔn)備好跃捣,所以需要計(jì)算出還需要多久去喚醒這條消息,也就是nextPollTimeoutMillis 這個(gè)參數(shù)夺蛇。如果當(dāng)前時(shí)間大于next消息的執(zhí)行時(shí)間,則執(zhí)行如下代碼
// 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;
}
這就是獲取消息的過程酣胀。首先mBlocked設(shè)置為false刁赦,阻塞的tag先關(guān)閉。如果當(dāng)前一條消息不為null闻镶,則把鏈表的最后一條消息賦值給到prevMsg的next消息甚脉,否則把鏈表的最后一條消息賦值給mMessages這個(gè)成員變量。最后把鏈表的最后一條消息刪除并且把當(dāng)前取到的msg置為正在使用铆农,把msg給return給調(diào)用者牺氨。再往后的源碼就是IdelHandler的相關(guān)使用了,我們后面再具體分析墩剖。分析到這猴凹,就有幾個(gè)結(jié)論了,我分別列舉出來
1岭皂、MessageQuene是單鏈表郊霎。
2、每次取出的是鏈表尾部的消息爷绘,當(dāng)這條消息未到執(zhí)行時(shí)間书劝,會(huì)計(jì)算出喚醒時(shí)間進(jìn)入nativePollOnce等待喚醒进倍,否則直接取出msg返回并從鏈表中刪除這條msg。
3购对、當(dāng)鏈表中沒有msg的時(shí)候猾昆,nextPollTimeoutMillis為-1,持續(xù)nativePollOnce等待喚醒骡苞。
分析完了取消息的過程垂蜗,繼續(xù)回到之前的loop函數(shù)。
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
msg.target指的是handler對(duì)象烙如,也就是說looper從MessageQuene取到消息之后會(huì)進(jìn)入到handler的dispatchMessage流程么抗。
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
我們假定不設(shè)置callBack,則最終進(jìn)入到了handleMessage亚铁,這就是new Handler里面handleMessage函數(shù)的執(zhí)行過程蝇刀。
接下去是sendMessage的過程分析。通過跟蹤sendMessage相關(guān)的源代碼徘溢,可以發(fā)現(xiàn)不管是sendMessage還是sendEmptyMessage都最終會(huì)進(jìn)入到如下函數(shù)
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);
}
其中SystemClock.uptimeMillis() 是表示系統(tǒng)開機(jī)到當(dāng)前的時(shí)間總數(shù)吞琐,單位是毫秒,但是然爆,當(dāng)系統(tǒng)進(jìn)入深度睡眠(CPU休眠站粟、屏幕休眠、設(shè)備等待外部輸入)時(shí)間就會(huì)停止曾雕,但是不會(huì)受到時(shí)鐘縮放奴烙、空閑或者其他節(jié)能機(jī)制的影響。官方解釋如下
/**
* Returns milliseconds since boot, not counting time spent in deep sleep.
*
* @return milliseconds of non-sleep uptime since boot.
*/
@CriticalNative
native public static long uptimeMillis();
下面的enqueueMessage函數(shù)就是MessageQuene中Message的添加過程剖张。
先截取一些代碼看一下
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
又看到了synchronized關(guān)鍵字切诀,所以enqueueMessage也是線程安全的。通過精讀上方代碼搔弄,我們可以得出如下結(jié)論
1幅虑、p==null表示第一次sendMessage,則鏈表中只有這個(gè)消息顾犹。
2倒庵、when==0表示handler執(zhí)行了sendMessageAtFrontOfQueue這個(gè)函數(shù),官方文檔標(biāo)注的是說把消息放在消息隊(duì)列的最前方炫刷,在單鏈表中指的是放到鏈表的尾部擎宝。
3、when<p.when指的是執(zhí)行時(shí)間小于鏈表尾部消息的執(zhí)行時(shí)間浑玛,則把這條消息置于鏈表尾部认臊。
當(dāng)p!=null&&when>0&&when>p.when的時(shí)候的執(zhí)行流程如下
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
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; // invariant: p == prev.next
prev.next = msg;
啟動(dòng)一個(gè)死循環(huán),直到p==null或者when<p.when的時(shí)候退出锄奢,目的是為了找到鏈表中when值大于當(dāng)前when的消息失晴。然后把p賦值給msg.next剧腻,msg賦值給pre.next。很顯然涂屁,這就是往鏈表中插入了一個(gè)元素书在。when值越小,越接近鏈表尾部拆又。分析至此儒旬,我們又可以得出結(jié)論了。
1帖族、結(jié)論前提:p!=null&&when>0&&when>p.when
2栈源、MessageQuene中每次進(jìn)入新消息,都需要根據(jù)msg.when進(jìn)行排序竖般,msg.when越小甚垦,越接近鏈表尾部。
至此為止涣雕,源碼層面的Handler機(jī)制全部分析完畢艰亮。
接下來,我們解答文出現(xiàn)的幾個(gè)問題:
1挣郭、為什么要有l(wèi)ooper死循環(huán)迄埃。
因?yàn)镴ava的Main函數(shù)執(zhí)行完就退出了。對(duì)于Android這樣的GUI程序兑障,肯定不能執(zhí)行完就退出侄非,所以引入了死循環(huán),讓線程一直執(zhí)行下去流译。
1逞怨、Looper.loop是一個(gè)死循環(huán),為什么不會(huì)卡死Android UI先蒋,為什么不會(huì)導(dǎo)致ANR。
Android 所有的 UI 刷新和生命周期的回調(diào)都是由 Handler消息機(jī)制完成的宛渐,就是說 UI 刷新和生命周期的回調(diào)都是依賴 Looper 里面的死循環(huán)完成的竞漾。其中,在UI的渲染過程中窥翩,會(huì)調(diào)用到如下代碼
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
其中有這么一行mHandler.getLooper().getQueue().postSyncBarrier();這就是說渲染view的過程也用到了handler機(jī)制业岁,并且渲染message的優(yōu)先級(jí)高于普通message。在Android各大組件的生命周期中寇蚊,利用了ActivityThread類中的H類來處理笔时,也是Handler機(jī)制。
2仗岸、nativePollOnce是什么允耿。
參考linux epoll
3借笙、文章開頭的問題答案是什么。
第二個(gè)函數(shù)會(huì)等待第一個(gè)函數(shù)執(zhí)行完了再執(zhí)行较锡。根據(jù)以上分析业稼,dispatchMessage發(fā)生在loop for循環(huán)中,消息是一條條取出的蚂蕴,msg.when小的排在鏈表尾部低散,會(huì)優(yōu)先被取出然后dispatch。handleMessage執(zhí)行完了之后骡楼,才會(huì)取下一條消息執(zhí)行熔号。
下一篇預(yù)告:ConcurrentHashMap源碼分析