Handler作為Android應(yīng)用層開發(fā)宽气,進程通信一大重點结执,可以說是使用最頻繁的一個機制食拜,不管是IntentService,ThreadHandler都繞不開它挚币。本文詳解Handler機制的內(nèi)部源碼
深入剖析Handler亮蒋,沒有看錯,比別人更深更精準妆毕!
看本文可以回答你這幾個問題:
UI線程的Looper在哪里創(chuàng)建慎玖?
MessageQueue真的是個隊列嗎?
延遲處理機制的原理笛粘?
Handler中的Message同步和MessageQueue同步趁怔?
@[toc]
一湿硝、Handler介紹
Handler在Android os包下,當我們創(chuàng)建Handler時润努,它會綁定一個線程关斜,并且創(chuàng)建一個消息隊列,通過發(fā)送Message或者Runnable對象到隊列并輪詢?nèi)〕銎探剑瑢崿F(xiàn)關(guān)聯(lián)痢畜。
我們常用的Handler功能是,定時執(zhí)行Runnable或者處理不同線程通信的問題鳍侣,比如UI線程和子線程等丁稀。
由此可見Handler內(nèi)部機制中的幾大元素:Handler,Message,MessageQueue,Looper,ThreadLocal等,接下來拱她,分別查看它的內(nèi)部源碼二驰。
二、Handler源碼剖析
Handler作為封裝對外的處理器秉沼,我們來看看它對外的接口內(nèi)部是做了哪些操作桶雀。
1. Handler構(gòu)造函數(shù):
它的構(gòu)造函數(shù),我歸納為三種方式,分別是:
1.傳入自定義Looper對象唬复,
2.繼承Handler實現(xiàn)Callback接口模式矗积,
3.默認創(chuàng)建的Looper模式,
其中2敞咧,3是我們常用的棘捣,當然1和2也能同時使用,callback接口中實現(xiàn)handleMessage休建,用于我們自定義Handler是實現(xiàn)回調(diào)用的乍恐。還有個被hide隱藏的傳參,async是否同步测砂,默認是不同步茵烈,且不支持設(shè)置同步模式。
可以傳入自定義的Looper砌些,Callback接口
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
常規(guī)的構(gòu)造方法如下:
其中FIND_POTENTIAL_LEAKS標簽是檢查“繼承類是否為非靜態(tài)內(nèi)部類”標簽呜投,我們知道,非靜態(tài)內(nèi)部類持有對象存璃,容易導致內(nèi)存泄漏的問題仑荐,可以查看我的《Android內(nèi)存優(yōu)化分析篇》
-
mAsynchronous可以看到一直是false
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; }
2. 創(chuàng)建Looper對象和mQueue消息隊列
由上構(gòu)造函數(shù)中調(diào)用Looper.myLooper();創(chuàng)建了Looper對象纵东,并取用了新創(chuàng)建Looper對象內(nèi)部的mQueue隊列粘招,詳解下Looper分析
3. sendMessage
-
其中sendEmptyMessage通過obtion新獲取了一個Message對象
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) { Message msg = Message.obtain(); msg.what = what; return sendMessageDelayed(msg, delayMillis); }
發(fā)送消息:sendMessageDelayed--->sendMessageAtTime--->enqueueMessage
注意到,在調(diào)用sendMessageAtTime時篮迎,傳入的時間值: 系統(tǒng)時鐘+delayMillis
其中將 msg.target標記為當前Handler對象
-
最終調(diào)用了MessageQueue的enqueueMessage男图,看后面MessageQueue分析
//----------1 public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 0); } //----------2 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 private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
4. removeMessages
從隊列刪除
5. post(Runnable r)
-
在getPostMessage中講Runnable封裝成了Message對象
public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); } private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m; }
6. dispatchMessage和handlerMessage
我們看到dispatchMessage調(diào)用了callback和handlerMessage分發(fā)Message結(jié)果
那么示姿,前面我們看到了經(jīng)常調(diào)用的sendMessage甜橱,那么回調(diào)是在什么時候調(diào)用的呢逊笆?
-
讓我們接下來一起看看Looper類吧。
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } } public void handleMessage(Message msg) {}
三岂傲、Looper源碼剖析
看looper做了什么难裆,首先看mylooper方法,還記得嗎镊掖,在Handler初始化時創(chuàng)建Looper對象調(diào)用的方法
1. myLooper方法
-
調(diào)用sThreadLocal取出一個looper對象
public static @Nullable Looper myLooper() { return sThreadLocal.get(); } // sThreadLocal.get() will return null unless you've called prepare(). static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
2. Looper.prepare()創(chuàng)建對象
上面看到mylooper從sThreadLocal取出乃戈,但是什么時候存的呢,looper又是如何創(chuàng)建亩进?
由下看出Looper通過prepare創(chuàng)建并存入sThreadLocal症虑,在構(gòu)造同時創(chuàng)建MessageQueue
標記成員mThread為當前線程
-
quitAllowed標識能否安全退出
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(); }
3. UI線程調(diào)用Handler,Looper怎么創(chuàng)建
prepareMainLooper:在當前線程初始化looper归薛,在ActivityThread調(diào)用谍憔,也就是我們創(chuàng)建Activity時已經(jīng)創(chuàng)建了Looper了
-
prepare(false):由于在ActivityThread創(chuàng)建,是不能安全退出的
/** * Initialize the current thread as a looper, marking it as an * application's main looper. The main looper for your application * is created by the Android environment, so you should never need * to call this function yourself. See also: {@link #prepare()} */ public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } } //-------->ActivityThread: Main: public static void main(String[] args) { --- Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } // End of event ActivityThreadMain. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); Looper.loop(); }
4. Looper.loop()
- UI線程創(chuàng)建Looper主籍,上ActivityThread中习贫,在調(diào)用prepare后接著調(diào)用Looper.loop
- loop通過 for (;;)死循環(huán),從queue中取下一則消息
- 其中 msg.target.dispatchMessage(msg);千元,在上面Handler中將handler對象傳給了looper
public static void loop() { 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; //--------------確保同一進程 Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } //--------------打印日志 final Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } //--------------從隊列中獲取分發(fā)消息延時 final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs; //--------------Trace標記苫昌,用于記錄message分發(fā)完成 final long traceTag = me.mTraceTag; if (traceTag != 0 && Trace.isTagEnabled(traceTag)) { Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); } final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis(); final long end; try { msg.target.dispatchMessage(msg); end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis(); } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } if (slowDispatchThresholdMs > 0) { final long time = end - start; if (time > slowDispatchThresholdMs) { Slog.w(TAG, "Dispatch took " + time + "ms on " + Thread.currentThread().getName() + ", h=" + msg.target + " cb=" + msg.callback + " msg=" + msg.what); } } if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); } //--------------充值message對象參數(shù) msg.recycleUnchecked(); } }
四、MessageQueue源碼剖析
MessageQueue主要分析插入和取出幸海,由下enqueueMessage插入方法看出祟身,它名字帶著Queue,但其實并不是,它實際是個單鏈表結(jié)構(gòu)物独,通過native操作指針袜硫,去進行msg的讀取操作。當然议纯,這更加快捷的實施取出父款,刪除和插入操作。
1. enqueueMessage
- msg.markInUse();標記當前msg正在使用
- 其中mMessages是可以理解為即將執(zhí)行的Message對象
- 將當前mMessages與新傳入的Msg的設(shè)置觸發(fā)時間對比瞻凤,如果新的Msg設(shè)置時間早憨攒,則將2者位置對調(diào),將新的排前面阀参,與之對比的mMessages排到其后肝集。反之,則與mMessages后一個對比時間蛛壳,依次類比杏瞻,插入到隊列中
- 其中所刀,如果msg事Asynchronous同步的,那么它只能等到上一個同步msg執(zhí)行完捞挥,才能被喚醒執(zhí)行浮创。
boolean enqueueMessage(Message msg, long when) {
...
synchronized (this) {
if (mQuitting) {
//------------->拋出一個IllegalStateException
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
//------------->標記當前msg正在使用
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 {
// 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;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
2. next取出
可以看出enqueueMessage和next都是同步的
通過循環(huán),把mMessages當前msg
比較當前時間和Msg標記時間砌函,如果早的話就設(shè)置一段指針查找超時時間
-
將msg標記為使用斩披,并取出消息返回
Message next() { //----->當消息輪詢退出時,mPtr指針找不到地址讹俊,返回空取不到對象 final long ptr = mPtr; if (ptr == 0) { return null; } //----->同步指針查找的時間垦沉,根據(jù)超時時間計算,比如當前未到msg的時間仍劈,指針會在一段計算好的超時時間后去查詢 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; //----->如果target為null,尋找下一個帶“同步”標簽的msg 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) { //----->比較當前時間和Msg標記時間厕倍,如果早的話就設(shè)置一段指針查找超時時間 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 first time idle, then get the number of idlers to run. // Idle handles only run if the queue is empty or if the first message // in the queue (possibly a barrier) is due to be handled in the future. 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; } }
3. quit操作
- 前面標記是否能安全退出,否則報錯
- 退出后喚醒指針贩疙,接觸msg的鎖
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
五讹弯、Message源碼剖析
Message主要是一個Parcelable序列號對象,封裝了不分信息和操作屋群,它構(gòu)造了一個對象池闸婴,這也是為什么我們一直發(fā)送msg,不會內(nèi)存爆炸的原因芍躏,來看看實現(xiàn)
1. obtain()
- 維持一個大小為50的同步線程池
- 這里可以看出Message是個鏈表結(jié)構(gòu)邪乍,obtain將sPool取出return Message,并對象池下一個msg標記為sPool
private static final int MAX_POOL_SIZE = 50;
...
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();
}
2.recycleUnchecked 回收消息
- 回收初始化當前msg
- 如果當前對象池大小小于MAX_POOL_SIZE对竣,則將初始化后的msg放到表頭sPool庇楞,sPoolSize++。
- 由此可以看出否纬,如果每次new新的Message傳入Handler吕晌,必然增加內(nèi)存消耗,通過obtain服用才是正確的做法
/**
* Recycles a Message that may be in-use.
* Used internally by the MessageQueue and Looper when disposing of queued Messages.
*/
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
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++;
}
}
}
3. Message標簽:是否使用临燃,同步標簽
public void setAsynchronous(boolean async) {
if (async) {
flags |= FLAG_ASYNCHRONOUS;
} else {
flags &= ~FLAG_ASYNCHRONOUS;
}
}
/*package*/ boolean isInUse() {
return ((flags & FLAG_IN_USE) == FLAG_IN_USE);
}
/*package*/ void markInUse() {
flags |= FLAG_IN_USE;
}
六睛驳、總結(jié)
廢了小半天功夫,整理了對Handler源碼的閱讀總結(jié)膜廊,雖然東西很多也很繁瑣乏沸,不過如果認真去看,是不是發(fā)現(xiàn)越深入就越有趣爪瓜,也越發(fā)發(fā)現(xiàn)Android源碼的嚴謹性蹬跃。平常至少簡單一用,只要深入了解才能更好地去使用它理解它铆铆,比如Message對象池的應(yīng)用蝶缀,這不就是享元模式嗎丹喻,希望大家都能有所體悟。