前言
很長的一段時間我一直在使用Handler
,主要是在處理非同步任務的時候來實現執行緒切換,不過對其原理和工作流程並沒有詳細的做過了解,以下我把從分析原始碼得到一些內容做出了一些總結。
從源分析Handler/MessageQueue/Looper的工作流程
首先來看下如下的示意圖,圖中描述的物件之間的基本通訊。
首先是Handler
物件傳送了一條Message
,然後訊息會被存放到一個列表(佇列:MessageQueue
)中,緊接著有一個叫做Looper
的物件會不停的去這個佇列中尋找有沒有新的訊息,有的話將訊息分配給Handler
物件進行處理(每一個Message
物件都會預設持有一個Handler
物件的引用,這個Handler
物件就是傳送這個Message
的物件,在Message
物件內部被定義為target
變數)。其具體的訊息會被放在target
所在的執行緒中執行。接下來詳細介紹訊息的收發和處理過程。
Handler的建立過程
首先先來看一下Handler的建構函式,如下圖,Handler一共向外提供了4個建構函式(其實在內部一共提供了是7個建構函式,只不過對外是隱藏的)。
//Step1
public Handler() {
this(null, false);
}
//Step2
public Handler(Callback callback, boolean async) {
//.........此處省略了部分程式碼
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
複製程式碼
可以看到的是我們在呼叫無引數構造方法時其實的呼叫的內部的具有兩個引數的構造方法,第一個要求傳入一個回撥實現(後便會具體介紹),第二個則是否非同步的識別符號。
在Step2
中可以看到幾個比較關鍵的內容,第一個關鍵點就是在我們新建的Handler物件內部儲存了一個Looper物件的引用,這個Looper.myLooper()
函式獲取的是當前執行緒所持有的Looper物件(執行緒中預設是沒有Looper物件的,只有呼叫Looper.propare()
函式之後才會在當前執行緒中建立一個唯一的Looper
物件,所以如果沒有則會丟擲一個異常,這個異常就是我們最初在子執行緒中使用Handler提示的異常資訊。);第二個關鍵點則是從Looper物件中拿到了一個訊息佇列物件mQueue
,這個物件是一個MessageQueue,它是在Looper被建立時建立的。
Looper/MessageQueue的建立過程/時機
MessageQueue
是跟隨Looper
的建立而建立的,在一個執行緒中只會存在一個Looper
物件,也就是說在一個執行緒中MessageQueue
也只會存在一個(理論上來說)。下面從原始碼中來印證如上所說。
1、來看Looper.prepare()
函式
這個方法僅僅是提供了一個入口方法,實際上呼叫的是內部的另一個prepare
方法。緊接著內部的這個prepare
方法通過new
的方式建立了一個Looper
物件,也就是在Step3
的內容。可以清楚的看到這裡為Looper
的內部變數mQueue
進行了賦值,也就是在這個時候MessageQueue
被建立。
在Step2的時候我們發現呼叫了一個叫做sThreadLocal
的變數的set
函式,這個ThreadLocal
並非是一個執行緒,它是用來儲存執行緒中資料的,具體可參考我的另一篇文章:ThreadLocal是什麼?
//Step1
public static void prepare() {
prepare(true);
}
//Step2
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));
}
//Step3
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
複製程式碼
在Looper物件中有一個非常重要的函式,那就是loop
了,中文翻譯過來就是迴圈的意思,這個函式會幫助我們不停的從MessageQueue
中來獲取Message
。
2、來看MessageQueue
目前來說,MessageQueue
的建立並沒有什麼值得我們關注的,它只是提供了一個先進先出的機制來幫助我們存取訊息,但是我們需要知道它所提供的兩個非常重要的方法,第一個就是enqueueMessage
,這個函式是用於將Message
物件新增到佇列中的,第二個就是next
函式,該函式是從訊息佇列中取訊息的,取出來後會立刻從佇列中移除。
Handler的訊息傳送過程
如下是一個基本的訊息傳送流程,基本使用這裡不在贅述。
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
//do something...
}
};
Message message = Message.obtain();
handler.sendMessage(message);
複製程式碼
1、走進handler.sendMessage(Message msg)
函式中來一探究竟。
//Step1
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
//Step2
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
//Step3
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);
}
複製程式碼
如上可以看到的是在我們呼叫handler.sendMessage(Message msg)
函式時,它會呼叫內部的sendMessageDelayed
函式,這個函式是用於傳送定時訊息的,因為sendMessage
傳送的都是需要立即被處理的訊息,所以傳入的就是0了,緊接著sendMessageDelayed
函式又呼叫了sendMessageAtTime
函式。
在這個sendMessageAtTime
函式中我們需要關注的是enqueueMessage
的呼叫,這個enqueueMessage
函式是幫助我們把訊息加入到MessageQueue
物件中。在如上Hanlder
建立過程的描述中,我們說了:這個訊息佇列(mQueue
物件)是在Handler
建立時從Looper
物件中獲取並儲存到區域性變數中的。
2、來看Handler中的enqueueMessage
函式
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
複製程式碼
這裡有兩點我們需要特別關注,第一個就是把當前Handler
物件的引用給了msg
的target
變數,這其實就是為之後的訊息處理提供處理者,在加入到訊息佇列之前會預設把Message
的target
設定為傳送Message
的Handler
物件,即便我們在建立Message
物件時設定了target
也會在enqueueMessage
函式中被重置,由此可以得出,Message
物件的傳送者即是Message
的處理者。
到這一步訊息已經被新增到了MessageQueue
物件中,至此Handler
的sendMessage
任務就算完成了,也就是說它成功的將訊息遞交給了MessageQueue
物件。
Message的處理過程
在上一段的結尾我們知道了Handler
的sendMessage
函式會把我們的Message
物件加入到一個叫做MessageQueue
的物件中,也就是說我們只是把訊息儲存了起來,單純的訊息儲存沒有任何意義,所以引入了Looper
物件來不停的從MessageQueue
中拿資料並且交給訊息的target
物件來進行處理。
1、來看Looper
的loop
函式
public static void loop() {
final Looper me = myLooper();//核心1
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;//核心2
//.......此處省略無關程式碼
boolean slowDeliveryDetected = false;
for (;;) {
//核心3
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
//.......此處省略無關程式碼
try {
msg.target.dispatchMessage(msg);//核心4(重點!!!!!!)
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
//.......此處省略無關程式碼
}
}
//核心1呼叫的myLooper函式
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
複製程式碼
以上就是Looper
的loop
函式了,為了便於觀看,這裡我刪減掉了無關的程式碼,並標註了4條較為重要的程式碼。
第一步:呼叫Looper
物件內部的myLooper()
函式,這個函式是從ThreadLocal
物件中取出當前所線上程的Looper
物件,它是我們在建立Looper
時儲存的Looper
物件,也就是我們在上邊介紹Looper
建立時看到的sThreadLocal.set(new Looper(quitAllowed));
。
第二步:拿到我們當前執行緒中持有的MessageQueue
物件,在上邊我們說了MessageQueue
是隨著Looper
的建立而被建立的。也就是說我們拿到的Looper
和MessageQueue
都是當前執行緒中的。至此你應該要知道Looper
和MessageQueue
在每一個執行緒中都是可以存在的,但是更要知道的是:在每一個執行緒中有且只有一個Looper
和MessageQueue
物件。如下我繪製了一張圖幫助更好記憶和理解。
第三步:從MessageQueue
中取出我們使用Handler.sendMessage
存放進取的訊息。
第四部:這一步其實是最核心的一部了,它通過呼叫Message
物件中的target
變數的dispatchMessage
函式,將訊息交給target
物件進行處理,在上邊我們說了target
物件就是傳送Message
物件的Handler
。所以最終的訊息處理會被放在該物件中被處理。
2、來看Handler
的dispatchMessage
函式
如下就是我們在上一步看到的loop
函式中呼叫的dispatchMessage
函式。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
/**
* Subclasses must implement this to receive messages.
* 譯:子類必須實現這個函式來接收訊息
*/
public void handleMessage(Message msg) {
}
複製程式碼
首先它會判斷Message
的callback
變數是否為NULL(在Message.obtain()
函式中可以傳入callback
),如果存在callback
那麼會優先處理Message
的callback
,否則會繼續判斷當前Handler
物件的callback
是否為NULL(這個callback
是在構造Handler物件時是可選傳入的),如果還不行那麼就呼叫Handler
中的handleMessage
函式,這也是我們常見的訊息處理方式,也就是我們在上邊重寫的handleMessage
函式。
這裡我們需要知道的就是訊息處理的優先順序:
1、由Message
物件的callback
處理(這個callback
是一個Runnable
物件)
2、由Handler
物件的mCallback
處理(這個callback
是Handler
物件中的一個介面提供了一個用於訊息處理的回撥public boolean handleMessage(Message msg);
)
3、由Handler
的handleMessage
處理(這個就是Handler
物件中的一個方法了,只有預設實現沒有任何程式碼,通常需要重寫)
另外需要知道的是:dispatchMessage
函式中所有的訊息都是在Handler
物件所處的執行緒中被執行的。
訊息的傳送和處理總結
1、呼叫Looper.prepare
函式
幫助我們建立Looper
物件和MessageQueue
物件。
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));//建立Looper物件並儲存當ThreadLocal中
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
複製程式碼
2、建立Handler
物件
在這一步主要是拿到當前執行緒的Looper
物件以及Looper
物件中的MessageQueue
物件並儲存其引用。
public Handler(Callback callback, boolean async) {
//........省略不重要程式碼
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
複製程式碼
3、呼叫Handler
的sendMessage
函式
該函式最終會走到Handler
物件中的enqueueMessage
中,將訊息儲存到當前執行緒的MessageQueue
物件中。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
複製程式碼
3、Looper
物件的loop
函式
當前執行緒的Looper
物件不斷的從它內部的MessageQueue
物件中取訊息,然後交給Message
的target
來做處理。
public static void loop() {
final Looper me = myLooper();//核心1
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;//核心2
//.......此處省略無關程式碼
boolean slowDeliveryDetected = false;
for (;;) {
//核心3
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
//.......此處省略無關程式碼
try {
msg.target.dispatchMessage(msg);//核心4(重點!!!!!!)
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
//.......此處省略無關程式碼
}
}
//核心1呼叫的myLooper函式
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
複製程式碼
4、Handler處理訊息
到這裡會根據優先順序來處理訊息,且訊息的執行是在當前Handler
所在的執行緒中。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
複製程式碼
至此核心的處理流程及已經完成了。
主執行緒中不用手動建立Looper的原因
Android主執行緒即ActivityThread
,在主執行緒的入口方法main
方法中呼叫了Looper的prepareMainLooper
函式,該函式是專門為主執行緒提供建立Looper
使用的。
public static void main(String[] args) {
//......省略無用程式碼
Looper.prepareMainLooper();
// Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
// It will be in the format "seq=114"
long startSeq = 0;
if (args != null) {
for (int i = args.length - 1; i >= 0; --i) {
if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
startSeq = Long.parseLong(
args[i].substring(PROC_START_SEQ_IDENT.length()));
}
}
}
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
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");
}
複製程式碼
總結
持有關係:
1、一個執行緒中只有一個Looper物件和一個MessageQueue 2、一個執行緒中可以有多個Handler物件 3、MessageQueue是包含在Looper中的
注意點:
1、Handler必須在持有Looper的執行緒中才能建立。 2、Handler的回撥優先順序(1、Message.callback2、Handler.callback、3、Handler.handleMessage)。 3、在使用Handler傳送Message時,Message的target會被預設設定為Message的傳送者。
最後
Handler
、Message
、MessageQueue
、Looper
組成了Android強大的訊息機制,以上只是簡述了其中的部分內容,還有很多的知識點等待日後進行挖掘。
原創文章,轉載請標明來源。