很長的一段時間我一直在使用Handler
,主要是在處理異步任務的時候來實現線程切換,不過對其原理和工做流程並無詳細的作過了解,如下我把從分析源碼獲得一些內容作出了一些總結。java
首先來看下以下的示意圖,圖中描述的對象之間的基本通訊。異步
首先是Handler
對象發送了一條Message
,而後消息會被存放到一個列表(隊列:MessageQueue
)中,緊接着有一個叫作Looper
的對象會不停的去這個隊列中尋找有沒有新的消息,有的話將消息分配給Handler
對象進行處理(每個Message
對象都會默認持有一個Handler
對象的引用,這個Handler
對象就是發送這個Message
的對象,在Message
對象內部被定義爲target
變量)。其具體的消息會被放在target
所在的線程中執行。接下來詳細介紹消息的收發和處理過程。async
首先先來看一下Handler的構造函數,以下圖,Handler一共向外提供了4個構造函數(其實在內部一共提供了是7個構造函數,只不過對外是隱藏的)。ide
//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被建立時建立的。oop
MessageQueue
是跟隨Looper
的建立而建立的,在一個線程中只會存在一個Looper
對象,也就是說在一個線程中MessageQueue
也只會存在一個(理論上來講)。下面從源碼中來印證如上所說。ui
一、來看Looper.prepare()
函數this
這個方法僅僅是提供了一個入口方法,實際上調用的是內部的另外一個prepare
方法。緊接着內部的這個prepare
方法經過new
的方式建立了一個Looper
對象,也就是在Step3
的內容。能夠清楚的看到這裏爲Looper
的內部變量mQueue
進行了賦值,也就是在這個時候MessageQueue
被建立。spa
在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
。
二、來看MessageQueue
目前來講,MessageQueue
的建立並無什麼值得咱們關注的,它只是提供了一個先進先出的機制來幫助咱們存取消息,可是咱們須要知道它所提供的兩個很是重要的方法,第一個就是enqueueMessage
,這個函數是用於將Message
對象添加到隊列中的,第二個就是next
函數,該函數是從消息隊列中取消息的,取出來後會馬上從隊列中移除。
以下是一個基本的消息發送流程,基本使用這裏不在贅述。
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
//do something...
}
};
Message message = Message.obtain();
handler.sendMessage(message);
複製代碼
一、走進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
對象中獲取並保存到局部變量中的。
二、來看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
對象。
在上一段的結尾咱們知道了Handler
的sendMessage
函數會把咱們的Message
對象加入到一個叫作MessageQueue
的對象中,也就是說咱們只是把消息保存了起來,單純的消息保存沒有任何意義,因此引入了Looper
對象來不停的從MessageQueue
中拿數據而且交給消息的target
對象來進行處理。
一、來看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
。因此最終的消息處理會被放在該對象中被處理。
二、來看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
函數。
這裏咱們須要知道的就是消息處理的優先級:
一、由Message
對象的callback
處理(這個callback
是一個Runnable
對象)
二、由Handler
對象的mCallback
處理(這個callback
是Handler
對象中的一個接口提供了一個用於消息處理的回調public boolean handleMessage(Message msg);
)
三、由Handler
的handleMessage
處理(這個就是Handler
對象中的一個方法了,只有默認實現沒有任何代碼,一般須要重寫)
另外須要知道的是:dispatchMessage
函數中全部的消息都是在Handler
對象所處的線程中被執行的。
一、調用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();
}
複製代碼
二、建立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;
}
複製代碼
三、調用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);
}
複製代碼
三、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();
}
複製代碼
四、Handler處理消息
到這裏會根據優先級來處理消息,且消息的執行是在當前Handler
所在的線程中。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
複製代碼
至此核心的處理流程及已經完成了。
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");
}
複製代碼
持有關係:
一、一個線程中只有一個Looper對象和一個MessageQueue 二、一個線程中能夠有多個Handler對象 三、MessageQueue是包含在Looper中的
注意點:
一、Handler必須在持有Looper的線程中才能建立。 二、Handler的回調優先級(一、Message.callback二、Handler.callback、三、Handler.handleMessage)。 三、在使用Handler發送Message時,Message的target會被默認設置爲Message的發送者。
Handler
、Message
、MessageQueue
、Looper
組成了Android強大的消息機制,以上只是簡述了其中的部份內容,還有不少的知識點等待往後進行挖掘。
原創文章,轉載請標明來源。