Android Handler機制使用,原始碼分析
目錄
前言
Thread+Handler機制實現執行緒間通訊的方式在安卓開發中的使用較為普遍。通常是將Handler是在主執行緒中建立,子執行緒拿到這個Handler向主執行緒中傳送訊息。Handler的執行依賴於MessageQueue和Looper,當然,既然是訊息機制,通常也需要用到Message,所以Handler、Looper、MessageQueue和Message的相符配合工作,原理就像是一個工廠的生產線,Looper是發動機,MessageQueue就是傳送帶,Handler是工人,Message就是待處理的產品,這麼理解會有一個關係概念,方便理解下面的內容。
一、Looper輪詢器
Looper是Handler機制執行工作的心臟,所以我們先搞懂Looper是怎麼回事是關鍵的第一步。我們知道應用的啟動通過main方法作為入口,這個main方法在主執行緒(ActivityThread)中,檢視這個類的main方法可以看到有兩個方法:Looper.prepareMainLooper()和Looper.loop()方法。
我們從這兩個方法去看清Looper的廬山真面目吧!go!go!
public static final void main(String[] args) {
...
//主執行緒中呼叫Looper.prepareMainLooper()方法建立Looper
Looper.prepareMainLooper();
if (sMainThreadHandler == null) {
sMainThreadHandler = new Handler();
}
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
//主執行緒中呼叫Looper.loop(),開始輪詢,取訊息
Looper.loop();
...
}
}
首先,ActivityThread首先呼叫的Looper.prepareMainLooper()方法,我們來看一下這個方法原始碼都做了些什麼?
在Looper類中
public static final void prepareMainLooper() {
prepare();
setMainLooper(myLooper());
if (Process.supportsProcesses()) {
myLooper().mQueue.mQuitAllowed = false;
}
}
原來,該方法中首先呼叫本類中的prepare()方法建立Looper物件,並且把該物件setMainLooper()繫結到當前執行緒中。在這裡既然是主執行緒呼叫的,那麼該Looper物件自然是在主執行緒當中。繼續看裡面的prepare()這個方法:
在Looper類中
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//把Looper繫結到當前執行緒
sThreadLocal.set(new Looper());
}
這個方法只做了一個事情,就是保證當前執行緒只能存在一個Looper物件,如果已經存在就會丟擲異常(Android規定一個執行緒只允許關聯一個Looper)。
繼續看new Looper()這個構造方法,我們可以看到:
private Looper() {
mQueue = new MessageQueue();
mRun = true;
mThread = Thread.currentThread();
}
Looper這個類的構造方法是private私有的,不允許外界直接new出來Looper物件,而最重要的是:在建立Looper的時候,同時建立了一個訊息佇列MessageQueue。所以請注意!MessageQueue是依賴Looper一起建立的。
然後,看ActivityThread呼叫Looper.loop()方法,開始輪詢訊息:
public static final void loop() {
Looper me = myLooper();
//通過Looper物件,獲取MessageQueue
MessageQueue queue = me.mQueue;
...
//死迴圈獲取訊息
while (true) {
//呼叫MessageQueue的next()方法取訊息,這個過程也是死迴圈
Message msg = queue.next();
if (msg != null) {
...
//取到訊息之後,交給傳送該訊息的Handler取處理訊息
msg.target.dispatchMessage(msg);
...
//回收訊息,在Message中維護的有一個訊息池
msg.recycle();
}
}
}
從上述原始碼中簡單的註解,我們可以直觀明白主執行緒的Looper和對應的MessageQueue之間最直接的關係了。但是我們帶著一個問題如何把Message訊息從子執行緒交給Handler在主執行緒中處理的?繼續分析msg.target.dispatchMessage()原始碼:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {//注意這個方法呼叫
return;
}
}
handleMessage(msg);
}
}
我們可以知道Looper.loop()方法裡的死迴圈是不斷地獲取MessageQueue
中的Message
,然後呼叫與Message
繫結的Handler
物件的dispatchMessage
方法。最後,我們終於明白原來Handler的handleMessage
就在這裡呼叫的,其實就是一個介面回撥啊。
二、Handler處理者
我們知道Handler的工作主要就是傳送和處理訊息。其實Handler的構造方法有多種,但都會獲取當前執行緒的Looper物件和MessageQueue物件。這個其實也沒什麼,看看就好:
//Handler的構造方法1
public Handler() {
...
//獲取當前執行緒的Looper物件
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 = null;
}
//Handler的構造方法2
public Handler(Callback callback) {
...
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;
}
//Handler的構造方法3
public Handler(Looper looper) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = null;
}
//Handler的構造方法4
public Handler(Looper looper, Callback callback) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
}
傳送訊息可以通過一系列send方法,也可以通過一系列post方法,不過post方法最終還是通過send方法去實現。
用send方法,最終也會呼叫一個方法如下:
//在Handler中
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
boolean sent = false;
MessageQueue queue = mQueue;
if (queue != null) {
//呼叫msg.target的方法把Message和傳送它的Handler繫結,所以Looper能夠把正確的Message交給傳送它的Handler處理
msg.target = this;
//呼叫MessageQueue的enqueueMessage方法把訊息加入訊息佇列
sent = queue.enqueueMessage(msg, uptimeMillis);
}
else {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
}
return sent;
}
可以看到,Handler傳送訊息的過程就是向訊息佇列中插入一條訊息。前面已經講到,MessageQueue呼叫next方法去輪詢訊息,那麼當MessageQueue拿到訊息之後,把訊息傳遞給Looper,最終交給Handler去處理,即dispatchMessage方法會被呼叫。此時,Handler開始處理訊息。值得一提的是,在訊息佇列中可能有不同Handler傳送的多個訊息,通過在傳送訊息的時候把Message和傳送它的Handler繫結,Looper就會把訊息正確的交給傳送它的Handler來處理。dispatchMessage方法如下:
//Handler中
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
Handler處理訊息的過程:
第一步,檢視Message的callback是否為null,不為null就通過handleCallback(msg)方法處理訊息。這裡的callback實際上就是一個Runnable物件。如果以post方式去傳送訊息,最終就會呼叫handleCallback(msg)方法去處理,這個方法內容為:
private final void handleCallback(Message message) {
message.callback.run();
}
第二步,檢查mCallBack是否為null,如果不為null就呼叫mCallBack的handleMessage(msg)方法。這個mCallBack是CallBack介面的子類物件,前面已經說過Handler的構造方法中有兩個可以用到這個CallBack。如果mCallBack為null,最終就會呼叫Handler的handleMessage(msg)方法,這個方法通常是在建立Handler時被使用者重寫的方法。
需要說明:在主執行緒當前我們建立Handler時並沒有自己建立Looper物件,這是因為主執行緒已經為我們建立好了;如果要在子執行緒當前建立Handler,一定要在之前建立Looper物件,即呼叫Looper.prepare()方法。
三、MessageQueue訊息佇列
前面的內容已經講了很多關於MessageQueue的東西,這裡就總結下了。MessageQueue主要包含兩個操作:插入訊息(enqueueMessage方法)和讀取訊息(next方法)。讀取的過程也伴隨著刪除操作。MessageQueue的的內部實際上是通過一個單連結串列的資料結構來維護訊息列表,這主要也是因為單連結串列在插入和刪除上比較有優勢。
final boolean enqueueMessage(Message msg, long when) {
...
Message p = mMessages;
if (p == null || when == 0 || when < p.when) {
// 當前傳送的message需要馬上被處理調,needWake喚醒狀態置true
msg.next = p;
mMessages = msg;
needWake = mBlocked; // new head, might need to wake up
} else {
// 當前傳送的message被排隊到其他message的後面,needWake喚醒狀態置為false
Message prev = null;
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
msg.next = prev.next;
prev.next = msg;
needWake = false; // still waiting on head, no need to wake up
}
}
// 是否喚醒主執行緒
if (needWake) {
nativeWake(mPtr);
}
return true;
}
enqueueMessage的主要操作其實就是單連結串列的插入操作,根據時間看當前傳送的Message是否需要馬上處理。這個enqueueMessage方法是Handler傳送訊息的時候呼叫。
下面來看next方法:
final Message next() {
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(mPtr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
final Message msg = mMessages;
if (msg != null) {
final long when = msg.when;
if (now >= when) {
mBlocked = false;
mMessages = msg.next;
msg.next = null;
if (Config.LOGV) Log.v("MessageQueue", "Returning message: " + msg);
return msg;
} else {
nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);
}
} else {
nextPollTimeoutMillis = -1;
}
...
}
注意"return msg",是next方法返回一個Message物件。next方法中也是採用阻塞的方式去獲取訊息佇列中的訊息,一旦有訊息立即返回並且將它從單連結串列中移除。如果沒有訊息就一直阻塞。前面已經提到,這個取訊息的next方法是在Looper的loop()方法中呼叫。
四、Message訊息載體
Message只有一個無參構造方法,但是Message有多個obtain靜態方法來返回Message物件。
採用哪種方式建立Message物件都可以,但是建議採用obtain方法來建立。這是因為Message通過在內部構建一個連結串列來維護一個被回收的Message物件池。當使用者呼叫obtain方法時會優先從池中獲取,如果池中沒有則建立新的Message物件。同時在使用完畢之後,進入池中以便於複用。這個在Looper.loop()方法可以看到一點端倪,在使用完畢時候呼叫了Message的recycle()方法。
下面是obtain方法建立Message物件的流程:
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
sPoolSize--;
return m;
}
}
return new Message();
}
五、小結
- 在應用啟動時,會開啟一個主執行緒建立一個Looper物件。Looper物件與當前執行緒繫結,使得不同執行緒間的Looper不能共享。
- 一個Thread執行緒對應多個Handler,一個Thread對應一個Looper物件、對應一個MessageQueue物件。
- Handler通過send方法或者post方法,把訊息加入訊息佇列MessageQueue中。
- 主執行緒中呼叫Looper的loop()方法,會開啟訊息迴圈,不斷的從訊息佇列中取出訊息。
- Looper拿到訊息之後呼叫Handler的dispatchMessage方法來處理訊息。
- 子執行緒中建立Handler需先建立Looper物件,Thread與Handler共享一個Looper與MessageQueue。
相關文章
- Android 原始碼分析(二)handler 機制Android原始碼
- Android--Handler機制及原始碼詳解Android原始碼
- Android Handler訊息機制原始碼解讀Android原始碼
- Android 9.0 原始碼_機制篇 -- 全面解析 HandlerAndroid原始碼
- android原始碼學習-Handler機制及其六個核心點Android原始碼
- Android Handler機制之Handler 、MessageQueue 、LooperAndroidOOP
- Android訊息機制HandlerAndroid
- android訊息機制—HandlerAndroid
- Android Handler機制之ThreadLocalAndroidthread
- Handler原始碼分析原始碼
- Android Handler 原始碼探索Android原始碼
- Android Handler 原始碼解析Android原始碼
- 原始碼分析:Android訊息處理機制原始碼Android
- Android Handler機制理解和AsyncTask使用小記Android
- 原始碼深度解析 Handler 機制及應用原始碼
- Android訊息機制Handler用法Android
- 基於原始碼分析 Android View 繪製機制原始碼AndroidView
- Android 8.1 Handler 原始碼解析Android原始碼
- Android Handler機制之Message及Message回收機制Android
- Handler訊息處理機制原始碼解析 上原始碼
- android 非同步通訊機制Handler的分析與運用Android非同步
- Android的Handler訊息機制 解析Android
- Android Handler機制之總目錄Android
- Android Handler 訊息機制詳述Android
- 基於原始碼分析 Android View 事件分發機制原始碼AndroidView事件
- 「Android」分析EventBus原始碼擴充套件Weex事件機制Android原始碼套件事件
- Android AccessibilityService機制原始碼解析Android原始碼
- Android原始碼學習之handlerAndroid原始碼
- Android 訊息機制:Handler、MessageQueue 和 LooperAndroidOOP
- Android Handler MessageQueue Looper 訊息機制原理AndroidOOP
- Dubbo 原始碼分析 - SPI 機制原始碼
- React原始碼分析 – 事件機制React原始碼事件
- Android進階;Handler訊息機制詳解Android
- Android 之 “只是想來談談 Handler 機制”Android
- Android Handler機制之記憶體洩漏Android記憶體
- Android-Handler訊息機制實現原理Android
- 全面剖析Android訊息機制原始碼Android原始碼
- HashMap擴容機制原始碼分析HashMap原始碼
- 從原始碼分析Hystrix工作機制原始碼