Android 從源碼角度分析消息處理機制(Handler,Looper,Message)
前言
在Android中,修改UI的操作必須要放入到主線程中。而我們的網絡請求往往是長時操作,需要放入到子線程進行請求。可以通過Handler
實現不同線程間的通信。對於如何實現的,網上有很多的教程或博文,也解釋的非常清楚,這裏不在多敘。
但在使用過程中,我們可能會有一些疑問:
爲什麼可以通過
Handler
實現不同的線程間通信。通過
sendXXX()
方法,是怎麼在handlerMessage()
中回調的爲什麼主線程不需要
Looper.prepare()
方法,而子線程需要Looper.prepare()
和Looper.loop()
方法。
可能很多博客都會從Handler
,Looper
,MessageQueue
他們之間的聯繫上,去解釋Android 中的消息處理機制,本篇博客意在從源碼的角度分析其通信原理。
源碼分析
在通常的編碼中,onCreate()
方法是一個Activity
的入口,我們往往在這裏做一些初始化操作。而對於一個程序來說,ActivityThread
的main()
方法,便是一個應用的入口,所以我們先看一下main()
方法的源碼
public static void main(String[] args) {
// 省略..
// 方法一,構造Looper 對象。
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
// 省略...
// 方法二,開始消息隊列的循環
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
在main()
方法中,存在兩個關鍵性的方法,分別是方法一和方法二。從英文命名上,我們可以大致猜出他們的意思,分別是準備主線程的Looper
對象和開始循環消息隊列。
進入到Looper.prepareMainLooper()
方法中,
public static void prepareMainLooper() {
// 構造Looper 方法
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
// 獲取當前的Looper()對象,進行賦值到sMainLooper
sMainLooper = myLooper();
}
}
prepare(false)
方法構造屬於當前線程的Looper
對象,傳入參數暫且不管,稍後會看其源碼。
然後判斷sMainLooper
對象是否爲null,如果不爲null就會拋出異常,異常的內容“主線程的Looper對象已經準備”,在這裏,通過字段的命名可以看出sMainLooper
是Looper
對象的靜態字段,並且保存主線程的Looper
對象。
那麼從prepareMainLooper()
和判斷條件可以得出,構造主線程的Looper
對象,保存對象到sMainLooper
中。如果sMainLooper
不爲null,表明重複調用,拋出異常。
那麼進入到prepare(false)
方法中,
private static void prepare(boolean quitAllowed) {
// 判斷是否創建過Looper 對象
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 創建Looper 對象,並保存
sThreadLocal.set(new Looper(quitAllowed));
}
先是通過sThreadLocal.get()
方法獲取當前線程的Looper
對象,如果不爲null,則拋出異常“一個線程只能創建一個Looper對象。”
如果不爲null,則創建Looper
對象並保存。
那麼sThreadLoacl
又是什麼呢,從命名上可以看出他是一個靜態字段,看一下聲明,
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
ThreadLocal
類型,該類是java.lang
中。看一下他的get()
和set()
方法,因爲我們主要是通過這兩個方法獲取對應的Looper
對象。
// get 方法
public T get() {
// 獲取當前所在的線程,很關鍵
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values != null) {
Object[] table = values.table;
int index = hash & values.mask;
if (this.reference == table[index]) {
return (T) table[index + 1];
}
} else {
values = initializeValues(currentThread);
}
return (T) values.getAfterMiss(this);
}
public void set(T value) {
// 獲取當前的線程
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values == null) {
values = initializeValues(currentThread);
}
values.put(this, value);
}
這兩個方法,看起來有點暈暈的,我們不深究其細節實現,明白其功能即可。
get()
能夠根據當前線程返回對應的Looper
對象
set()
能夠根據當前線程保存對應的Looper
對象。
繼續回到prepare()
方法中,我們通過get()
判斷,通過set()
進行保存。其中通過new Looper(quitAllowed)
方法,構造Looper
對象,看一下他的實現
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
在Looper
的構造方法中,構造了一個MessageQueue()
對象並保存,同時保存當前線程的對象。那麼關鍵詞出現了,MessageQueue,這時網上很多分析中出現的關鍵類,負責消息的隊列。由此可以看出,Looper
對象中保存有MessageQueue
的實例。
在這裏,我們需要注意的是mQueue
和mThread
,從命名可以看出都是成員變量,即他和Looper
的實例都是一一對應的。
public final class Looper {
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // guarded by Looper.class
// 每一個looper 對應的消息隊列
final MessageQueue mQueue;
// looper 對應的線程
final Thread mThread;
}
將字段的具體聲明貼出,以便理解。
那麼,到此,整個Looper.prepareMainLooper()
方法就大致分析完了。
Looper.loop()
在ActivityThread.main()
中,最後調用了Looper.loop()
方法,看一下此方法的實現。
public static void loop() {
// 1.獲取Looper 對象
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
// 2.獲取Looper對象對應的消息隊列
final MessageQueue queue = me.mQueue;
// 3.無線循環,遍歷消息隊列
for (;;) {
Message msg = queue.next(); // 4.循環消息隊列
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
msg.target.dispatchMessage(msg);// 5.分發消息
msg.recycleUnchecked(); // 6.回收資源
}
}
中間省略了一些代碼,自上往下分析:
1的地方首先獲取到當前線程的Looper
對象。
2的時候獲取到Looper
對象的MessageQueue
實例,在創建Looper
時,構造方法中創建了和他一一對應的MessageQueue
對象,主要是作爲消息隊列。
然後無限for循環,遍歷消息隊列。
通過MessageQueue
的next()
方法獲取到Message
對象,具體的5和6,我們需要看一下Handler
的實現後進行理解。
Handler的實現
通過上面的分析,我們知道在主線程中構造了Looper
對象並且開始輪詢。那麼另一個關鍵人物Handler
,勢必要分析他們直接的聯繫。
看一下Handler
的構造方法
public Handler(Callback callback, boolean async) {
// ...
// 獲取當前線程的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;
}
通常的使用,都是直接使用new Handler()
,最終仍會調用此構造方法,默認爲null和false。
在這個方法中,handler
保存了當前線程的looper
的實例和消息隊列。
通常的使用中通過handler.sendMessage()
向主線程發送消息。看一下實現
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);
}
最終會調用此方法,在這個方法中,獲取消息隊列,並調用enqueueMessage(queue, msg, uptimeMillis)
,看一下這個方法,
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
// msg.target = handler
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
該方法,將當前handler
對象賦值到msg.target
中,並且調用MessageQueue
的enqueueMessage()
方法。
到此,對Handler
的分析就差不多結束了。回過頭,在ActivityThread.main()
中,Looper.loop()
我們還有一點沒有分析,就是無限循環的處理,再看一下源碼
// 3.無線循環,遍歷消息隊列
for (;;) {
Message msg = queue.next(); // 4.循環消息隊列
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
msg.target.dispatchMessage(msg);// 5.分發消息
msg.recycleUnchecked(); // 6.回收資源
}
我們知道,msg.targer
是我們構造的handler
,那麼繞了一圈有回到了自身的handler
上,調用handler.dispatchMessage(msg)
方法,看一下其實現,
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
其中由幾個判斷,最終不通過的時候調用handlerMessage(msg)
方法。看一下之前的判斷
msg.callback
指定的是什麼,在什麼情況下不等於null
呢,在使用handler
的時候,還有一種使用方式及handler.post(Runnable r)
,看一下他的實現
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;
}
這兩個方法比較清晰,從這個地方可以看出,msg.callback
會調用我們的Runnable
對象。
而對於mCallback
對象,在構造方法中傳入的,因爲平常沒有用過,都是使用無參的構造函數,基本上都爲null。
那麼,通過以上的分析,整個流程就比較清晰了:
ActivityThread.main()
中調用Looper.prepareMainLooper()
和Looper.loop()
方法。Looper.prepareMainLooper()
方法創建Looper
對象,同時Looper
對象中會保存與其對應的MessageQueue
對象。Looper.loop()
調用之後對MesageQueue
對象進行輪詢。- 在創建
Handler
的時候,會保存當前線程的Looper
對象和消息隊列MessageQueue
。 - 通過調用
handler.sendXXX()
將消息推送到消息隊列。 - 消息隊列中實際上調用的是
handler.handlerMessage()
方法,產生了聯繫。
子線程和主線程的使用區別
在實際使用中,子線程如果想要構造handler
對象,必須調用Looper.prepare()
和Looper.loop()
方法。而主線程不需要,爲什麼呢?
如果對於上面的源碼分析理解清楚了可以清楚的知道,因爲主線程中已經在ActivityThread.main()
即應用打開的初期初始化過Looper
對象並開始了消息隊列。