一篇文章深入分析Handler源碼
首先附上我的幾篇其它文章鏈接感興趣的可以看看,如果文章有異議的地方歡迎指出,共同進步,順便點贊謝謝!!!
Android framework 源碼分析之Activity啓動流程(android 8.0)
Android studio編寫第一個NDK工程的過程詳解(附Demo下載地址)
面試必備1:HashMap(JDK1.8)原理以及源碼分析
面試必備2:JDK1.8LinkedHashMap實現原理及源碼分析
Android事件分發機制原理及源碼分析
View事件的滑動衝突以及解決方案
Android三級緩存原理及用LruCache、DiskLruCache實現一個三級緩存的ImageLoader
Handler概述
Handler是一種通信機制,只不過在Android我們常用來更新UI,接下來我將分別從Message、MessageQueue、Looper、handler以及ThreadLocal的源碼去深入理解handler的執行流程。
Message :消息對象
Message消息對象,它是數據的載體,內部有幾個屬性,可以讓我們攜帶數據;而Message通過內部有一個池機制,可以讓我們複用Message對象 ,而這個消息池的最大容量 MAX_POOL_SIZE = 50,消息池是通過鏈表數據結構來組織起來的。
消息池:想要了解池機制我們需要從Message的 obtain() 和 recycle()兩個核心方法入手了池機制,首先我們要先看看Message中的幾個重要的成員變量,next;存的是我們當前個消息對象的下一個消息對象的地址,同過next屬性構建出一個鏈表結果的消息池。
/**
* 此處我只是沾出Message的幾個常用的重要屬性,其他屬性我們不常用在這裏沒貼出來有需要的大家可以去源碼看
*/
public int what;
public int arg1;
public int arg2;
public Object obj;//上面四個我們常用攜帶數據標識
long when; //標識當前消息的觸發時間
Handler target;//存儲發送消息的hanndler
Message next;//存的是我們當前消息對象的下一個消息對象的地址,通過next屬性構建出一個鏈表結果的消息池
private static Message sPool; //sPool屬性:我們當前池的頭指針位置 ,即只存出鏈表的第一個消息地址
private static final Object sPoolSync = new Object();//同步鎖防止線程污染
private static int sPoolSize = 0;//消息池大小
private static final int MAX_POOL_SIZE = 50;//消息池最大容量
接下來我們看看obtain()方法的源碼:
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
* if (sPool爲空){
* return; new Message(); //無可複用的消息,消息池爲空
*}else {
* return 從消息池中獲取; }
*/
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;//取出表頭的Message對象返回
sPool = m.next;//講鏈表後移,記錄新的表頭消息
m.next = null;//移除第一個
m.flags = 0; // clear in-use清除標記
sPoolSize--;//鏈表長度減去1
return m;
}
}
return new Message();
}
接下來我們看看recycle()方法的源碼:
先判斷當前消息對象是否在使用中,如果在使用中,則該消息對象無法回收 直接return, 否則調用recycleUnchecked()方法回收消息。需要注意的是recycle方法不需要我們手動調用,它的調用實在Looper的loop()方法中自動調用,詳細過程將在Looper源碼中進行分析
/**
* Return a Message instance to the global pool.
* <p>
* You MUST NOT touch the Message after calling this function because it has
* effectively been freed. It is an error to recycle a message that is currently
* enqueued or that is in the process of being delivered to a Handler.
* </p>
*/
public void recycle() {
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}//在使用中的消息對象無法回收 直接return
return;
}
recycleUnchecked();
}
/**
* Recycles a Message that may be in-use.
* Used internally by the MessageQueue and Looper when disposing of queued Messages.
* recycleUnchecked回收未在使用中的消息對象,實現鏈表加1
*/
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;//以上操作把要回收的message對象的成員變量回歸初始值
//以下是重點:實現鏈表連接池的加1,將message存入消息池中,
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;//先把當前回收的message的next指向消息池的鏈表頭
sPool = this;//再將自己當做新的表頭
sPoolSize++;//長度加1
}
}
}
通過以上分析,我們可以知道Mesage是消息對象handler中消息數據的載體,只是它內置消息池實現消息對象的複用以避免new 對象時造成的內存讓費。
MessageQueue:消息隊列的源碼分析
MessageQueue消息隊列用於存儲handler發送的Message對象,本質上還是通過Message對象的next屬性組織起一個鏈表雙向鏈表,具有先進先出的特性。接下來我將從它的存儲(入隊)enqueueMessage()方法和取出(出隊)next() 兩個方法進行分析,需要注意的是出隊和入隊操作都是按照Message的when屬性進行。
首先我們先分析next()方法源碼 :
- 調用時機是在Looper.loop()方法的死循環中獲取消息,根據觸發時間(Message.when屬性)判斷。
- 如果觸發時間到了,那麼就將當前消息取出return給Looper進行處理, 如果觸發時間沒到,那麼就運算出一個時間的差值 ——我們此刻距離消息觸發還需要多久(nextPollTimeoutMillis)
- 在下一次循環的時候,會調用netive方法——nativePollOnce(nextPollTimeoutMillis),nativePollOnce方法中有一個wait動作,讓線程暫停nextPollTimeoutMillis時間值,等到nextPollTimeoutMillis時間達到以後,循環繼續執行
/**
*取出下一個消息的動作
*/
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();
}
//調用本地native的nativePollOnce(ptr, nextPollTimeoutMillis)方法休眠
//nextPollTimeoutMillis=msg.when-當前系統時間
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.
//如果當前系統時間< msg.when計算時間差值,在上面調用
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.否則取出當前消息返回給Looper
//消息隊列中移除當前消息並更新相關狀態,鏈表的移除即prevMsg 當前的消息直接指向它
//的下一個消息的下一個msg.next
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;//否則取出當前消息返回給Looper
}
} 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;
}
}
enqueueMessage()方法的源碼 調用時機:是在Handler中enqueueMessage()方法調用,至於詳細的調用過程我將在Handler的源碼中進行分析,這裏不再贅述。
/**
*MessageQueue中
*enqueueMessage方法源碼
*/
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {//msg.target在message源碼中說過他是用來存儲發送當前消息的Handler對象
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
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;
}//上面是對Message使用的異常情況的判斷(非核心代碼)
msg.markInUse();//更新當前message的使用狀態
msg.when = when;//取出入隊消息的觸發時間
Message p = mMessages;//表頭消息
//是否需要喚醒。因爲在next中nativePollOnce
//(nextPollTimeoutMillis)中有一個wait動作 ,線程會暫停運行nextPollTimeoutMillis時間值
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.隊列爲空時把當前Message作爲新表頭
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;//記入上一個Message
for (;;) {//死循環前後比較時間 根據when去確定要插入的位置
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) {
//喚醒線程,調用native方法
nativeWake(mPtr);
}
}
return true;
}
到此MessageQueue的enqueueMessage()和next()入隊和出隊分析完成。
Looper :消息輪詢器
Looper :消息輪詢器:
- 作用: 不停的從MessageQueue中獲取未處理的消息交給handler去處理
- Looper 通過prepare()方法 獲取和實例化Looper對象,本身是一個單例模式只能通過prepare()實例化
- Looper 的loop()方法是通過一個死循環不斷的從MessageQueue中讀取未處理的消息,其實就是在不停對MessageQueue進行next()動作,不斷的拿到我們需要處理的下一個消息進行處理
- 每一個Looper都自帶一個自己的MessageQueue,在自己的構造器中就已經進行了實例化
- next()方法取出消息後將消息交給當前消息的msg.target. dispatchMessage ()方法處理,在上面Message的源碼中分析過msg.target存儲的是發送該消息的handler對象,即調用handler. dispatchMessage ()方法處理消息
- 在next方法的最後調用msg.recycleUnchecked()方法處理回收該消息對象
以上便是Looer的大致流程,接下來分析prepare()和loop()方法的源碼
prepare()方法源碼分析
/** Initialize the current thread as a looper. prepare方法源碼
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
public static void prepare() {
prepare(true);
}
/**
*藉助ThreadLoacl來幫我們進行Looper對象的存和取,實現線程之間數據的隔離存儲,在handler中通過ThreadLocal.get();取出looper對象,詳細內容將會在Handler源碼中進行分析
*/
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {//線程有且只有一個Looper對象
throw new RuntimeException("Only one Looper may be created per thread");
}
//實例化Looper對象並存儲到 sThreadLocal
sThreadLocal.set(new Looper(quitAllowed));
}
loop()方法源碼分析:
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
* 去除部分代碼看主流程
* final Looper me = myLooper();//通過 myLooper()獲取Looper對象
* final MessageQueue queue = me.mQueue;//通過Looper獲取消息隊列
* for (;;) {//開啓死循環
* Message msg = queue.next(); // Messagequeue.next()取出消息
* try {
* msg.target.dispatchMessage(msg);//交給Handler處理
* }
* msg.recycleUnchecked();//回收消息對象
*/
public static void loop() {
final Looper me = myLooper();//通過 myLooper()獲取Looper對象
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;//通過Looper獲取消息隊列
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
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;
}
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
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);//);//交給Handler處理
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);
}
msg.recycleUnchecked();//;//回收消息對象
}
}
myLooper()方法源碼:
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();//從sThreadLocal中獲取Looper對象
}
到此Looper的工作流程完畢,至於使用ThreadLocal去存儲獲取Looper對象的原因有:
- 幫我們實現一個線程有且只有一個Looper對象
- 數據隔離:可以讓我們在當前線程的任意地方獲取到Looper對象,並保證處於同一個線程中的類,取到的是同一個Looper對象,進而操作同一個MessageQueue。只有當我們能夠保證發送消息和接收消息所操作的MessageQueue是同一個消息隊列的時候,程序才能運轉正常。
Handler:消息的發送者 和 消息的最終處理者
Handler的發送和處理分別是通過Handler的enqueueMessage()和dispatchMessage()兩個核心方法進行的。結下來將從“構造起”和以上兩個方法的源碼入手
Handler的構造方法初始化數據:
- 通過 Looper.myLooper()實例化了當前線程的Looper對象 mLooper
- 並且通過looper對象獲取MessageQueue mQueue
- 從構造器中可以看出初始化Handler必須先初始化Looper對象,而在我們平時使用過程中主線程並沒有先調用 Looper.myLooper()實例化Looper對象的原因是:在應用程序啓動時ActivityThread主線程的main()方法中實例化了主線程中的Looper對象,具體源碼稍後分析。
構造器有重載但是最終都會調用如下方法:
/**
* @hide
*/
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();//獲取當前線程的Looper對象
if (mLooper == null) {
throw new RuntimeException(
//從此處可以看出初始化Handler必須先初始化Looper對象
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;//通過Looper對象獲取Looper中的消息隊列MessageQueue
mCallback = callback;
mAsynchronous = async;
}
消息的發送: 最終是通過調用MessageQueue調用消息隊列的enqueueMessage()方法進行存儲消息,即入隊操作。我們無論是通過調用handler的sendMessage(Message msg)、sendEmptyMessage(int what)、sendMessageDelayed(msg, delayMillis)等方法還是調用post(Runnable r)、postDelayed( )等post相關方法最終都會調用sendMessageAtTime()方法,即handler發送消息無論通過send相關方法還是post相關方法,最終都是調用sendMessageAtTime()方法發送消息。
至於如何調用到endMessageAtTime方法,非常簡單,就是通過方法重載。該過程源碼在這裏不再贅述,我們直接看sendMessageAtTime()方法的源碼:
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;//step1獲取構造器中初始化的MessageQueue
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}//調用enqueueMessage方法
return enqueueMessage(queue, msg, uptimeMillis);
}
由上面看出sendMessageAtTime最終掉用了handler的enqueueMessage()方法發送消息,enqueueMessage()源碼如下:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
/**
注意: msg.target = this;將當前Handler對象存儲到當前消息的 msg.target中,照應了Looper.loop()方法中拿到消息後通過msg.target獲取當前handler.調用msg.target.dispatchMessage()方法處理消息,即最終又交給了當前handler處理消息*/
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//最終調用MessageQueue的enqueueMessage(msg, uptimeMillis);進行消息入隊操作
//queue.enqueueMessage方法在上面已經分析
return queue.enqueueMessage(msg, uptimeMillis);
}
用dispatchMessage()方法處理消息, 是在Looper的loop()方法中調用處理消息,源碼如下:
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
//處理消息時判斷是否攔截消息mCallback.handleMessage(msg),返回true則不會執行下面的
//handleMessage(msg),即攔截了消息
return;
}
}
//如果不攔截,回掉handleMessage方法處理消息
handleMessage(msg);
}
}
到此handler的發送和處理的源碼分析完成。
爲什麼在主線程中初始化Handler對象不需要先初始化Looper
簡單分析一下ActivityThread主線程中的main方法的源碼:
//此方法是應用程序的主入口
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
SamplingProfilerIntegration.start();
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("<pre-initialized>");
//在應用程序啓動時就初始化了主線程的Looper對象
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();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
總結
到此爲止handler的源碼分析完畢。總結一下,我們通過handler發送消息的過程就是通過Looper獲取MessageQueue調用eqeueMessage方法入隊的過程,而處理消息的過程就是通過Looper的loop方法不斷的從當前線程的MessagQueue中取出消息交給發送消息的handler對象調用dispatchMessage方法處理消息,因此handler在哪個線程實例化就在哪個線程處理消息。
這是第一次寫博客,希望這篇文章對大家有所幫助,裏面的不足之處請大家留言指正。我後續也會繼續分享和編寫更多幹貨,請大家多多支持和點贊!!!謝謝