主要的內容包括Handler的機制以及四個組成部分和原始碼的分析
下面的程式碼分析都是基於Android8.0 - Oreo的原始碼
1. 訊息機制簡介
在應用啟動時,會執行main()
方法,main()
會建立一個Looper
物件,然後開啟一個死迴圈,目的是不斷從訊息佇列MessageQueue
裡面取出Message
物件並處理。
在Android中使用訊息機制,會優先想到的是Handler。Handler可以輕鬆的將一個任務切換到Handler所在的執行緒去執行。在多執行緒的應用場景中,可以將工作執行緒中需要更新UI的操作資訊傳遞到主執行緒去執行,從而實現工作執行緒更新UI的操作,最終實現非同步訊息的處理。
2. Handler機制模型
訊息機制主要包含Handler、Message、MessageQueue,Looper這四個類。
- Handler:訊息輔助類。主要功能將
Message
物件傳送到MessageQueue
中,同時將自己的引用賦值給Message#target
(Handler.sendMessage())。也可以實現handleMessage()
方法處理回撥。 - Message:訊息實體。需要傳遞的訊息也可以傳遞資料。
- MessageQueue:訊息佇列。**內部實現並不是佇列,而是利用單連結串列去實現因為在插入和刪除資料有優勢。**用於儲存Handler發給來的訊息(
Message
)以及取出。內部使用單連結串列實現
- Looper:與執行緒繫結,不止侷限於主執行緒,繫結的執行緒來處理
Message
。不斷迴圈執行Looper.loop()
,從MessageQueue
中讀取Message
,按分發機制將訊息分發出去給目標處理(將Message
發到Handler.dispatchMessage
方法去處理)。
3. Handler執行流程
工作流程:非同步通訊準備==>訊息入隊==>訊息迴圈==>訊息處理-
非同步通訊準備
假定在主執行緒建立Handler,則會直接在主執行緒中建立
Looper
,MessageQueue
和Handler
物件。Looper和MessageQueue物件均屬於其建立執行緒
(由主執行緒建立則屬於主執行緒)。建立Looper
時會自動建立MessageQueue
物件,建立好MessageQueue
物件後,Looper
自動進入迴圈。Handler
自動繫結Looper
以及MessageQueue
。Looper
物件的建立方法一般通過Looper.prepareMainLooper()
和Looper.prepare()
方法。 -
訊息入隊
工作執行緒通過
Handler
傳送Message
到MessageQueue
中。訊息內容一般是UI操作,通過Handler.sendMessage(Message message)
或Handler.post(Runable r)
傳送。加入MessageQueue
一般通過MessageQueue.enqueueMessage(Message msg,long when)
操作。 -
訊息迴圈
分為訊息出隊和訊息分發兩個步驟
- 訊息出隊:
Looper
從MessageQueue
中迴圈取出Message
- 訊息分發:
Looper
將取出的Message
分發給建立訊息的Handler
訊息迴圈過程中,
MessageQueue
為空,則執行緒堵塞 - 訊息出隊:
-
訊息處理
Handler
接受發過來的Message
並處理。
4. Handler使用過程的注意點
-
在工作執行緒中建立自己的訊息佇列時必須要呼叫
Looper.prepare()
,並且在一個執行緒中只可以呼叫一次,然後需要呼叫Looper.loop()
,開啟訊息迴圈。在開發過程中基本不會呼叫上述方法,因為預設會呼叫主執行緒的Looper,然後一個執行緒中只能有一個Looper物件和一個MessageQueue。
-
要注意Handler可能引起的記憶體洩漏(在下面會介紹到為何會引發洩漏)。
錯誤的寫法:
private final Handler mHandler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); } }; 複製程式碼
非靜態的內部類和匿名內部類都會隱式的持有其外部類的引用,而靜態內部類不會持有外部類的引用。
正確的寫法:
繼承
Handler
時候要麼放在單獨的類檔案中,要麼直接使用靜態內部類。//需要在靜態內部類中呼叫外部類時,可以直接使用 `弱引用` 進行處理 private static final class MyHandler extends Handler{ private final WeakReference<MyActivity> mWeakReference; public MyHandler(MyActivity activity){ mWeakReference = new WeakReference<>(activity); } @Override public void handlerMessage(Message msg){ super.handlerMessage(msg); MyActivity activity = mWeakReference.get(); } } //呼叫方法 private MyHandler mHandler = new MyHandler(this); 複製程式碼
5. Handler原始碼解析
-
建立迴圈器物件(
Looper
)和建立訊息佇列物件(MessageQueue
)建立Looper物件主要有兩個方法:
Looper.prepareMainLooper()
和Looper.prepare()
建立MessageQueue物件方法:建立Looper物件時會自動建立MessageQueue
// 原始碼位置:../core/java/android/os/Looper.java final MessageQueue mQueue; final Thread mThread; //Looper物件建立時會自動建立一個MessageQueue物件。 private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); } //為當前執行緒(子執行緒)建立一個Looper物件 需要在子執行緒中主動呼叫該方法 public static void prepare() { prepare(true); } private static void prepare(boolean quitAllowed) { //判斷sThreadLocal是否為null,不為空則直接跑出異常 可以保證一個執行緒只可以呼叫一次prepare方法 if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); } //為主執行緒建立一個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
物件時會自動建立MessageQueue
物件 -
主執行緒的Looper物件是自動生成的,而子執行緒需要呼叫
Looper.prepare()
建立Looper
物件建立主執行緒是呼叫了
ActivityThread
的main()
方法。然後按照流程呼叫了
Looper.prepareMainLooper()
和Looper.loop()
。所以主執行緒不需要呼叫程式碼生成Looper物件。//原始碼位置: ../core/java/android/app/ActivityThread.java public static void main(String[] args) { ... Looper.prepareMainLooper(); Looper.loop(); ... } 複製程式碼
-
Handler的主要作用是(
在主執行緒更新UI
),所以Handler主要是在主執行緒建立的。 -
Looper與Thread是通過
ThreadLocal
關聯的。由於ThreadLocal
是與執行緒直接關聯的,參考prepare()
。 -
子執行緒建立Handler物件:無法在子執行緒直接呼叫Handler無參構造方法Handler建立時需要繫結Looper物件 。需要使用
HandlerThread
。
-
-
開啟Looper即訊息迴圈
建立了
Looper和MessageQueue
物件後,自動進入訊息迴圈,使用Looper.loop()
方法開始訊息迴圈。//原始碼位置:../core/java/android/os/Looper.java public static void loop(){ //現獲取Looper例項,保證呼叫loop時已有Looper,否則丟擲異常 final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } //獲取對應Looper例項建立的MessageQueue物件 final MessageQueue queue = me.mQueue; ... //開啟訊息迴圈-無限迴圈 for (;;) { //從MessageQueue取出Message物件 Message msg = queue.next(); // might block //取出訊息為null,則退出迴圈 if (msg == null) { // No message indicates that the message queue is quitting. return; } //把Message分發給相應的target try { msg.target.dispatchMessage(msg); end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis(); } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } //釋放訊息佔據的資源 msg.recycleUnchecked(); } } 複製程式碼
-
建立Handler物件
建立Handler物件即可以進行訊息的傳送與處理
//原始碼位置:.../core/java/android/os/Handler.java //Handler預設構造方法 public Handler() { this(null, 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()); } } //從當前執行緒的ThreadLocal獲取Looper物件 mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } //獲取當前Looper的訊息佇列 mQueue = mLooper.mQueue; mCallback = callback; //設定訊息是否為非同步處理方式 mAsynchronous = async; } public Handler(Looper looper, Callback callback, boolean async) { mLooper = looper; mQueue = looper.mQueue; mCallback = callback; mAsynchronous = async; } 複製程式碼
Handler的無參構造方法會預設關聯當前執行緒的Looper物件和MessageQueue物件,設定callback回撥方法為null,且訊息處理方式為同步處理。
-
建立訊息物件
Handler傳送Message並且進入MessageQueue迴圈,建立方式分為兩種
new Message()
和Message.obtain()
。通常使用Message.obtain()
。這種方式有效避免建立重複Message物件。//建立訊息物件 Message msg = Message.obtain(); msg.what = 1; msg.obj = "test"; //原始碼位置 .../core/java/android/os/Message.java /** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}). */ //new Message 方法 public Message() { } private static final Object sPoolSync = new Object(); //維護一個Message池,用於複用Message物件 private static Message sPool; //obtain方法 直接從池內獲取Message物件,避免new佔用記憶體 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; } } //無可複用物件,則重新new獲取 return new Message(); } 複製程式碼
-
傳送訊息(Message)
Handler主要有以下幾種傳送訊息的方式:
sendMessage(Message msg)
sendMessageDelayed(int what, long delayMillis)
post(Runnable r)
postDelayed(Runnable r, long delayMillis)
sendMessageAtTime(Message msg, long uptimeMillis)
最終都是會呼叫到
sendMessageAtTime(Message msg, long uptimeMillis)
然後繼續呼叫到enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)
放入MessageQueue//原始碼位置:.../core/java/android/os/Handler.java //post方法 public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); } public final boolean postAtTime(Runnable r, long uptimeMillis) { return sendMessageAtTime(getPostMessage(r), uptimeMillis); } public final boolean postAtTime(Runnable r, Object token, long uptimeMillis) { return sendMessageAtTime(getPostMessage(r, token), uptimeMillis); } public final boolean postDelayed(Runnable r, long delayMillis) { return sendMessageDelayed(getPostMessage(r), delayMillis); } //利用post()方式傳送訊息,需要轉換為Message向下傳遞 private static Message getPostMessage(Runnable r, Object token) { Message m = Message.obtain(); m.obj = token; //將runnable賦值到callback上 以便後續判斷是post還是sendMessage方式傳送的訊息 m.callback = r; return m; } //sendMessage方法 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物件 MessageQueue queue = mQueue; //獲取物件為空 丟擲異常 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); } //該方法為了 向MessageQueue插入Message private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { // 把當前的Handler設定為 訊息標記位 即把訊息派發給相對應的Handler例項 msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } //呼叫MessageQueue的enqueueMessage方法 return queue.enqueueMessage(msg, uptimeMillis); } 複製程式碼
//原始碼位置:..core/java/android/os/MessageQueue.java //內部是一個單連結串列有序序列,由 Message.when 作為排序依據,該值為一個相對時間。 boolean enqueueMessage(Message msg, long when) { ... synchronized (this) { //正在退出 回收Message 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; // p == null判斷當前佇列中是否有訊息,插入訊息作為佇列頭 // when == 0||when < p.when 佇列當前處於等待狀態 喚醒佇列 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; //迴圈到佇列尾部或者出現一個when小於當前Message的when 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.when
表示期望該訊息被分發的時間即SystemClock.uptimeMillis() + delayMillis
。SystemClock.uptimeMills
代表自系統開機到呼叫到該方法的時間差。 Message.when
利用時間差來表達期望事件分發的時間,所以使用的是一個相對時間。
-
獲取訊息
傳送了訊息後,MessageQueue維護了訊息佇列,在Looper中通過
loop()
不斷獲取Message。通過next()
獲取Message.//原始碼位置:..core/java/android/os/MessageQueue.java Message next(){ //該引數用於確定訊息佇列中是否有訊息 下一個訊息到來前需要等待的時長 int nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); } //該方法位於native層 若nextPollTimeoutMillis為-1 代表訊息佇列處於等待狀態 阻塞操作 nativePollOnce(ptr, nextPollTimeoutMillis); ... synchronized (this) { ... Message msg = mMessages; 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 { // Got a message. mBlocked = false; if (prevMsg != null) { prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null; //標記訊息使用狀態 flag |= FLAG_IN_USE msg.markInUse(); //返回一條訊息 return msg; } } else { // No more messages. nextPollTimeoutMillis = -1; } //訊息正在退出 if (mQuitting) { dispose(); return null; } } } } 複製程式碼
-
分發訊息
分發訊息到對應的Handler例項並根據傳入的Message做對應的操作
//原始碼位置:.../core/java/android/os/Handler.java public void dispatchMessage(Message msg) { //若callback不為空,則代表使用了post(Runnable r)方式傳送了訊息,執行handleCallback方法 if (msg.callback != null) { handleCallback(msg); } else { //代表使用了sendMessage()方式傳送了訊息,呼叫handleMessage方法 if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } } //建立Handler例項時複寫 自定義訊息處理方法 public void handleMessage(Message msg) { } //直接回撥runnable物件的run() private static void handleCallback(Message message) { message.callback.run(); } 複製程式碼
總結:
msg.target.dispatchMessage(msg)
中msg.target
指向的就是對應Handler例項,- 訊息分發的優先順序:
- Message的回撥方法
message.callback.run()
- Handler中Callback的回撥方法
mCallback,handleMessage(msg)
- Handler的預設方法
handleMessage()
- Message的回撥方法
-
Message回收
上面講到了新建Message推薦使用
obtain()
,因為可以有效的複用訊息,其中裡面複用的就是sPool
變數,它是在Message回收的時候進行賦值的。//原始碼位置 .../core/java/android/os/Message.java /*package*/ boolean isInUse() { return ((flags & FLAG_IN_USE) == FLAG_IN_USE); } public void recycle() { //正在使用 無法回收 if (isInUse()) { if (gCheckRecycle) { throw new IllegalStateException("This message cannot be recycled because it " + "is still in use."); } return; } recycleUnchecked(); } 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放在了列表裡,快取的物件由obtain()拿出來複用 synchronized (sPoolSync) { if (sPoolSize < MAX_POOL_SIZE) { next = sPool; sPool = this; sPoolSize++; } } } 複製程式碼
-
Looper退出
Looper.loop()
內部由一個無限迴圈組成,預設情況下不會退出迴圈。需要退出就需要呼叫quit()
或者quitSafely()
。//原始碼位置 .../core/java/android/os/Looper.java public void quit() { mQueue.quit(false); } public void quitSafely() { mQueue.quit(true); } //原始碼位置 .../core/java/android/os/MessageQueue.java 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); } } //直接移除MessageQueue中的所有訊息 private void removeAllMessagesLocked() { Message p = mMessages; while (p != null) { Message n = p.next; //回收未被處理的訊息 p.recycleUnchecked(); p = n; } //由於訊息為null 則return 出無限迴圈 mMessages = null; } //直接移除未處理的訊息 已經在執行的繼續處理 private void removeAllFutureMessagesLocked() { final long now = SystemClock.uptimeMillis(); Message p = mMessages; if (p != null) { //還未處理的Message if (p.when > now) { removeAllMessagesLocked(); } else { Message n; for (;;) { n = p.next; if (n == null) { return; } if (n.when > now) { break; } p = n; } //不接收後續訊息 p.next = null; do { p = n; n = p.next; p.recycleUnchecked(); } while (n != null); } } } 複製程式碼