Android多執行緒之Handler、Looper與MessageQueue原始碼解析
本文的目的是來分析下 Android 系統中以 Handler、Looper、MessageQueue 組成的非同步訊息處理機制,通過原始碼來了解整個訊息處理流程的走向以及相關三者之間的關係
需要先了解以下幾個預備知識
- Handler:UI 執行緒或者子執行緒通過 Handler 向 MessageQueue(訊息佇列) 傳送 Message
- MessageQueue:通過 Handler 傳送的訊息並非是立即執行的,需要存入一個訊息佇列中來依次執行
- Looper:Looper 不斷從 MessageQueue 中獲取訊息並將之傳遞給訊息處理者(即是訊息傳送者 Handler 本身)進行處理
- 互斥機制:可能會有多條執行緒(1條 UI 執行緒,n 條子執行緒)向同一個訊息佇列插入訊息,此時就需要進行同步
Handler 傳送訊息的形式主要有以下幾種形式,其最終呼叫的都是 sendMessageAtTime()
方法
public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg, 0);
}
public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis){
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
可以看到 sendMessageAtTime()
方法中需要一個已初始化的 MessageQueue
型別的全域性變數 mQueue
,否則程式無法繼續走下去
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);
}
而 mQueue
變數是在建構函式中進行初始化的,且 mQueue
是成員常量,這說明 Handler
與 MessageQueue
是一一對應的關係,不可更改
如果建構函式沒有傳入 Looper
引數,則會預設使用當前執行緒關聯的 Looper
物件,mQueue
需要依賴於從 Looper
物件中獲取,如果 Looper
物件為 null ,則會直接丟擲異常,且從異常資訊 Can`t create handler inside thread that has not called Looper.prepare()
中可以看到,在向 Handler
傳送訊息前,需要先呼叫 Looper.prepare()
public Handler(Callback callback, boolean async) {
···
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;
}
走進 Looper
類中,可以看到,myLooper()
方法是從 sThreadLocal
物件中獲取 Looper
物件的,sThreadLocal
物件又是通過 prepare(boolean)
來進行賦值的,且該方法只允許呼叫一次,一個執行緒只能建立一個 Looper 物件,否則將丟擲異常
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
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));
}
此處除了因為prepare(boolean)
多次呼叫會丟擲異常導致無法關聯多個 Looper 外,Looper 類的建構函式也是私有的,且在建構函式中還初始化了一個執行緒常量 mThread
,這都說明了 Looper 只能關聯到一個執行緒,且關聯之後不能改變
final Thread mThread;
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
那麼 Looper.prepare(boolean)
方法又是在哪裡呼叫的呢?查詢該方法的所有引用,可以發現在 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();
}
}
最後定位到 ActivityThread
類的 main()
方法
看到 main()
函式的方法簽名,可以知道該方法就是一個應用的起始點,即當應用啟動時, 系統就自動為我們在主執行緒做好了 Handler
的初始化操作, 因此在主執行緒裡我們可以直接使用 Handler
如果是在子執行緒中建立 Handler
,則需要我們手動來呼叫 Looper.prepare()
方法
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();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
回到最開始,既然 Looper
物件已經由系統來為我們初始化好了,那我們就可以從中得到 mQueue
物件
public Handler(Callback callback, boolean async) {
···
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can`t create handler inside thread that has not called Looper.prepare()");
}
//獲取 MessageQueue 物件
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
mQueue
又是在 Looper
類的建構函式中初始化的,且 mQueue
是 Looper
類的成員常量,這說明 Looper 與 MessageQueue 是一一對應的關係
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
sendMessageAtTime()
方法中在處理 Message
時,最終呼叫的是 enqueueMessage()
方法
當中,需要注意 msg.target = this
這句程式碼,target 物件指向了傳送訊息的主體,即 Handler 物件本身,即由 Handler 物件發給 MessageQueue 的訊息最後還是要交由 Handler 物件本身來處理
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) {
//target 物件指向的也是傳送訊息的主體,即 Handler 物件
//即由 Handler 物件發給 MessageQueue 的訊息最後還是要交由 Handler 物件本身來處理
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
因為存在多個執行緒往同一個 Loop 執行緒的 MessageQueue 中插入訊息的可能,所以 enqueueMessage()
內部需要進行同步。可以看出 MessageQueue 內部是以連結串列的結構來儲存 Message 的(Message.next),根據 Message 的延時時間的長短來將決定其在訊息佇列中的位置
mMessages 代表的是訊息佇列中的第一條訊息,如果 mMessages 為空,說明訊息佇列是空的,或者 mMessages 的觸發時間要比新訊息晚,則將新訊息插入訊息佇列的頭部;如果 mMessages 不為空,則尋找訊息列隊中第一條觸發時間比新訊息晚的非空訊息,並將新訊息插到該訊息前面
到此,一個按照處理時間進行排序的訊息佇列就完成了,後邊要做的就是從訊息佇列中依次取出訊息進行處理了
boolean enqueueMessage(Message msg, long when) {
//Message 必須有處理者
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) {
// 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;
}
下面再看下 MessageQueue 是如何讀取 Message 並回撥給 Handler 的
在 MessageQueue 中訊息的讀取其實是通過內部的 next()
方法進行的,next()
方法是一個無限迴圈的方法,如果訊息佇列中沒有訊息,則該方法會一直阻塞,當有新訊息來的時候 next()
方法會返回這條訊息並將其從單連結串列中刪除
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) {
// 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 {
// 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;
}
}
next()
方法又是通過 Looper
類的 loop()
方法來迴圈呼叫的,而 loop()
方法也是一個無限迴圈,唯一跳出迴圈的條件就是 queue.next()
方法返回為null ,細心的讀者可能已經發現了,loop()
就是在 ActivityThread
的 main()
函式中呼叫的
因為 next()
方法是一個阻塞操作,所以當沒有訊息也會導致 loop()
方法一隻阻塞著,而當 MessageQueue 一中有了新的訊息,Looper 就會及時地處理這條訊息並呼叫 Message.target.dispatchMessage(Message)
方法將訊息傳回給 Handler 進行處理
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
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;
// 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);
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();
}
}
看下 Handler 物件處理訊息的方法
/**
* 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);
}
}
如果 msg.callback
不為 null ,則呼叫 callback 物件的 run()
方法,該 callback 實際上就是一個 Runnable 物件,對應的是 Handler 物件的 post()
方法
private static void handleCallback(Message message) {
message.callback.run();
}
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;
}
如果 mCallback
不為 null ,則通過該回撥介面來處理訊息,如果在初始化 Handler 物件時沒有通過建構函式傳入 Callback
回撥介面,則交由 handleMessage(Message)
方法來處理訊息,我們一般也是通過重寫 Handler 的 handleMessage(Message)
方法來處理訊息
最後來總結下以上的內容
一、在建立 Handler 例項時要麼為建構函式提供一個 Looper 例項,要麼預設使用當前執行緒關聯的 Looper 物件,如果當前執行緒沒有關聯的 Looper 物件,則會導致丟擲異常
二、Looper 與 Thread ,Looper 與 MessageQueue 都是一一對應的關係,在關聯後無法更改,但 Handler 與 Looper 可以是多對一的關係
三、Handler 能用於更新 UI 有個前提條件:Handler 與主執行緒關聯在了一起。在主執行緒中初始化的 Handler 會預設與主執行緒繫結在一起,所以此後在處理 Message 時,handleMessage(Message msg)
方法的所線上程就是主執行緒,因此 Handler 能用於更新 UI
四、可以建立關聯到另一個執行緒 Looper 的 Handler,只要本執行緒能夠拿到另外一個執行緒的 Looper 例項
new Thread("Thread_1") {
@Override
public void run() {
Looper.prepare();
final Looper looper = Looper.myLooper();
new Thread("Thread_2") {
@Override
public void run() {
Handler handler = new Handler(looper);
handler.post(new Runnable() {
@Override
public void run() {
//輸出結果是:Thread_1
Log.e(TAG, Thread.currentThread().getName());
}
});
}
}.start();
Looper.loop();
}
}.start();
更多的原始碼解讀請看這裡:Java_Android_Learn
相關文章
- Android Handler機制之Handler 、MessageQueue 、LooperAndroidOOP
- Android訊息機制全面解析(Handler,MessageQueue,Looper,Threadlocal)AndroidOOPthread
- Android入門教程 | Handler,Looper與MessageQueue使用與分析AndroidOOP
- Android Handler MessageQueue Looper 訊息機制原理AndroidOOP
- Android 訊息機制:Handler、MessageQueue 和 LooperAndroidOOP
- 從源分析Handler、MessageQueue、LooperOOP
- Handler,Looper,MessageQueue,Message直接聯絡OOP
- 三劍客 Handler、Looper 和 MessageQueueOOP
- Android 進階 ———— Handler系列之建立子執行緒HandlerAndroid執行緒
- Android Handler 原始碼解析Android原始碼
- Android Handler與Looper原理簡析AndroidOOP
- Android多執行緒之執行緒池Android執行緒
- ConcurrentHashMap原始碼解析,多執行緒擴容HashMap原始碼執行緒
- Android 8.1 Handler 原始碼解析Android原始碼
- Handler全家桶之 —— Handler 原始碼解析原始碼
- Netty原始碼解析一——執行緒池模型之執行緒池NioEventLoopGroupNetty原始碼執行緒模型OOP
- Java多執行緒之Thread原始碼分析Java執行緒thread原始碼
- 執行緒池執行模型原始碼全解析執行緒模型原始碼
- Android原始碼學習之handlerAndroid原始碼
- 併發與多執行緒之執行緒安全篇執行緒
- 執行緒與多執行緒執行緒
- Java執行緒池ThreadPoolExecutor原始碼解析Java執行緒thread原始碼
- Java原始碼解析 ThreadPoolExecutor 執行緒池Java原始碼thread執行緒
- Java原始碼解析 - ThreadPoolExecutor 執行緒池Java原始碼thread執行緒
- 一個執行緒可以有幾個Looper?幾個Handler?從Looper.prepare()來看看關於Looper的一些問題執行緒OOP
- 從原始碼的角度解析執行緒池執行原理原始碼執行緒
- Adnroid原始碼學習筆記:Handler 執行緒間通訊原始碼筆記執行緒
- Android 9.0 原始碼_機制篇 -- 全面解析 HandlerAndroid原始碼
- Android/java 多執行緒(一)-Thread的使用以及原始碼分析AndroidJava執行緒thread原始碼
- 多執行緒系列之 執行緒安全執行緒
- iOS 多執行緒之執行緒安全iOS執行緒
- Java多執行緒之執行緒中止Java執行緒
- 執行緒池之ScheduledThreadPoolExecutor執行緒池原始碼分析筆記執行緒thread原始碼筆記
- 執行緒池之ThreadPoolExecutor執行緒池原始碼分析筆記執行緒thread原始碼筆記
- Handler系列原始碼解析原始碼
- Android Handler 原始碼探索Android原始碼
- Android如何保證一個執行緒最多隻能有一個LooperAndroid執行緒OOP
- Android程式框架:執行緒與執行緒池Android框架執行緒