從原始碼分析Android Handler 訊息機制
看了Danny老師的視訊,從原始碼層學習了一下Handler訊息機制中幾個重要的類(Handler、MessageQueue、Looper和Message),從之前的一知半解到現在終於弄明白這個Handler訊息機制到底是怎麼進行工作的了,在這裡記錄下來,同時很感謝Android-ReadTheFuckingSourceCode/blob/master/android/Android-Handler%E6%B6%88%E6%81%AF%E6%9C%BA%E5%88%B6.md" target="_blank" rel="nofollow,noindex">一張圖看明白Android Handler 訊息機制 這篇博文,裡面講解的也很詳細很清楚,本文的原始碼大都來自這篇博文中。有需要視訊的小夥伴我可以分享給你。
概述
訊息機制主要是指 Handler 的執行機制以及 Handler 同 MessageQueue 和 Looper配合工作的過程。 Handler 的主要作用是將某個任務切換到 Handler 所在的執行緒中去執行。
Looper
每個執行緒中最多隻能有一個 Looper 物件,由 Looper 來管理此執行緒裡的 MessageQueue (訊息佇列)。
可以通過 Looper.myLooper() 獲取當前執行緒的 Looper 例項,通過 Looper.getMainLooper() 獲取主(UI)執行緒的 Looper 例項。
Lopper 會以無限迴圈的形式去查詢是否有新訊息,如果有就處理訊息,否則就一直等待著
Handler
你可以構造 Handler 物件來與 Looper 溝通,通過 push 傳送新訊息到 MessageQueue 裡;或者通過 handleMessage 接收 Looper 從 MessageQueue 取出來訊息。
MessageQueue
MessageQueue是一個訊息佇列,內部儲存了一組訊息,以佇列的形式對外提供插入和 刪除的工作,內部採用單鏈表的資料結構來儲存訊息列表。
ActivityThread
我們經常提到的主執行緒,也叫UI執行緒,它就是 ActivityThread,主執行緒啟動會預設初始化一個 Looper 並建立 Handler。
一個執行緒中只有一個 Looper 例項,一個 MessageQueue 例項,可以有多個 Handler 例項。
ThreadLocal
一個執行緒內部的資料儲存類,通過它可以在指定執行緒中儲存資料,資料儲存後,只有在指定執行緒中可以獲取到儲存的資料,對於其他執行緒來說無法獲得資料。
對於 Handler 來說,它需要獲取當前執行緒的 Looper ,而 Looper 的作用於就是執行緒並且不同的執行緒具有不同的 Looper ,通過 ThreadLocal 可以輕鬆實現執行緒中的存取。
ThreadLocal原理:不同執行緒訪問同一個ThreadLoacl的get方法,ThreadLocal的get方法會從各自的執行緒中取出一個數組,然後再從陣列中根據當前ThreadLocal的索引去查詢對應的Value值。
原始碼分析
-
Looper
在Looper
的構造方法中建立訊息佇列並儲存當前執行緒;
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed);//建立訊息佇列 mThread = Thread.currentThread();//儲存當前執行緒 }
在prepare
方法中將建立的Lopper
放入到ThreadLocl
中——sThreadLocal.set(new Looper(quitAllowed));
(所以在使用Looper
時先調prepare
方法進行初始化);Looper
也提供初始化主執行緒的Looper
和獲取主執行緒的Looper
:prepareMainLooper()、getMainLooper()
;Looper
的loop()
:在當前執行緒中開啟輪詢,首先從ThreadLocal
中取出當前執行緒的looper
物件——sThreadLocal.get()
(沒有調prepare方法會拋異常),再從looper
物件中取出訊息佇列——final MessageQueue queue = looper.mQueue
,進行死迴圈不斷的取出訊息queue.next()
,取出的訊息交給handler
進行執行分發訊息的操作——msg.target.dispatchMessage(msg);
,訊息已經分發後進行回收操作——msg.recycleUnchecked()
Looper原始碼
public final class Looper { static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); //每個執行緒都會有一個ThreadLocal 用來儲存 Looper物件(裡面包含了主執行緒和 MessageQueue) private static Looper sMainLooper;// 主執行緒的 Looper final MessageQueue mQueue;//儲存訊息佇列 final Thread mThread;//儲存主執行緒 public static void prepare() {//為當前執行緒建立 Looper prepare(true); } private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { //一個執行緒只能有一個 Looper, 否則丟擲異常 throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed));//將建立的 Looper 放入 ThreadLocal } //初始化主執行緒的 Looper public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } } //獲取主執行緒的 Looper public static Looper getMainLooper() { synchronized (Looper.class) { return sMainLooper; } } //在當前執行緒中開啟輪詢 public static void loop() { final Looper me = myLooper();//從 ThreadLocal 中取出當前執行緒的 Looper 物件 if (me == null) { //Looper 沒有呼叫 Looper.prepare() 初始化,丟擲異常 throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } final MessageQueue queue = me.mQueue;//從 Looper 物件中取出訊息佇列 for (;;) {//死迴圈 Message msg = queue.next(); // 不斷的取出訊息 if (msg == null) { // No message indicates that the message queue is quitting. return; } ... try { msg.target.dispatchMessage(msg); //取出訊息的 target (也就是 Handler),執行分發訊息的操作 } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } ... msg.recycleUnchecked();//訊息已經分發,進行回收操作 } } public static @Nullable Looper myLooper() { return sThreadLocal.get();//從 ThreadLocal 中取出當前執行緒的 Looper 物件 } private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed);//建立訊息佇列 mThread = Thread.currentThread();//儲存當前執行緒 } public void quit() { mQueue.quit(false);//直接退出訊息迴圈,不管是否還有訊息 } public void quitSafely() { mQueue.quit(true);//執行完所有的訊息,退出訊息迴圈 } ... }
-
MessageQueue
主要說說訊息是怎麼加入訊息佇列和從訊息佇列中取出的,分別對應兩個方法enqueueMessage() 和next()
。
訊息在入隊的時候,如果訊息佇列裡面沒有訊息,或者訊息的執行時間比裡面的訊息早,就把這條訊息設定成第一條訊息(一般不會出現這種情況,因為系統一定會有很多訊息),如果訊息佇列裡面有訊息,找到訊息佇列裡面的最後一條訊息,把訊息新增到最後。
取出訊息時取出一條訊息,訊息佇列往後移動一個,並將此訊息比較為已使用。
加入訊息的原始碼
boolean enqueueMessage(Message msg, long when) { if (msg.target == null) { 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; } msg.markInUse(); msg.when = when; Message p = mMessages; boolean needWake; if (p == null || when == 0 || when < p.when) { // 如果訊息佇列裡面沒有訊息,或者訊息的執行時間比裡面的訊息早,就把這條訊息設定成第一條訊息。 //一般不會出現這種情況,因為系統一定會有很多訊息。 msg.next = p; mMessages = msg; needWake = mBlocked; } else {//如果訊息佇列裡面有訊息 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; }
取訊息的原始碼
Message next() { int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); } nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this) { final long now = SystemClock.uptimeMillis(); Message prevMsg = null; Message msg = mMessages;//拿到當前的訊息佇列 if (msg != null && msg.target == null) { //處理非同步的訊息,暫不討論 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 { //取出一條訊息,訊息佇列往後移動一個 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; } ... } }
-
Message 是什麼
public final class Message implements Parcelable { public int what;//訊息型別,標識訊息的作用 public int arg1;//整型引數1 public int arg2;//整型引數2 public Object obj;//複雜物件引數 public Messenger replyTo; public int sendingUid = -1; /*package*/ static final int FLAG_IN_USE = 1 << 0;//標記訊息已使用 /** If set message is asynchronous */ /*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1;//標記訊息是否非同步 /** Flags to clear in the copyFrom method */ /*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE; /*package*/ int flags;//訊息當前標記 /*package*/ long when;//訊息執行時間 /*package*/ Bundle data; /*package*/ Handler target;//Handler 用於執行 handleMessage(); /*package*/ Runnable callback;//訊息是一個Runnable // sometimes we store linked lists of these things /*package*/ 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;//訊息最大數量 //從整個Messge池中返回一個新的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(); } ... }
-
Handler 是怎麼與 Looper 和 MessageQueue 一起搭配工作的
Handler
的構造方法靈活方便,可以指定回撥方法,也可以關聯哪個執行緒的Looper
,所以處理訊息
時根據建立Handler時引數及訊息體的不同選擇不同的方法執行:如果訊息體是Runnable
就執行run()
;如果建立Handler
時傳入了Callback
,就執行Callback
裡面的邏輯;如果上述兩種都沒有實現,就執行handleMessage
的邏輯。
傳送訊息:Handler
提供了4種傳送訊息的方法,分別是:
-
傳送
Runnable
訊息的post(Runnable r)
-
般更新 UI 時傳送的訊息,延時時間為0
sendMessage(Message msg)
-
傳送延時訊息
sendMessageDelayed(Message msg, long delayMillis)
-
傳送指定傳送時間的訊息
sendMessageAtTime(Message msg, long uptimeMillis)
Handler 的原始碼:
public class Handler { public interface Callback { public boolean handleMessage(Message msg); } public void handleMessage(Message msg) {} /** * 訊息處理 */ public void dispatchMessage(Message msg) { if (msg.callback != null) {//如果訊息體是 Runnable 就執行 run() handleCallback(msg); } else { if (mCallback != null) { //如果建立 Handler 時傳入了 Callback,就執行 Callback 裡面的邏輯 if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg);//如果上述兩種都沒有實現,就執行 handleMessage 的邏輯 } } public Handler() { this(null, false); } public Handler(Callback callback) { this(callback, false); } public Handler(Looper looper) {//可以指定關聯哪個執行緒的 Looper this(looper, null, false); } public Handler(Looper looper, Callback callback) { this(looper, callback, false); } public Handler(boolean async) { this(null, async); } /** * 主執行緒呼叫的構造方法,主執行緒已經呼叫了 Looper.prepareMainLooper(); * * @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( "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue;//把 Handler 的 mQueue 指向 Looper 中的 mQueue mCallback = callback; mAsynchronous = async; } /** * 第二種構造方法,專門給子執行緒中建立 Handler 時使用的 * * @hide */ public Handler(Looper looper, Callback callback, boolean async) { mLooper = looper; mQueue = looper.mQueue; mCallback = callback; mAsynchronous = async; } //傳送 Runnable 訊息 public final boolean post(Runnable r){ returnsendMessageDelayed(getPostMessage(r), 0); } private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m; } //一般更新 UI 時傳送的訊息,延時時間為0 public final boolean sendMessage(Message msg){ return sendMessageDelayed(msg, 0); } //傳送延時訊息 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); } private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; //把位元組傳入 Message 中一起傳送 //Looper 中需要使用 Handler 來執行 dispatchMessage 方法 if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); } }