快速了解消息循環(huán)場景
首先一句話總結(jié)一下上面這些概念:
Looper是為一個Thread添加一個事件循環(huán)(Message Loop)
MessageQueue是Looper中管理Message的隊列
Message是事件循環(huán)中的事件對象
Handler是用來創(chuàng)建Message并且管理發(fā)送Message的
接下來用通俗的語言來描述一下這些對象運行的場景:
Looper是一個死循環(huán),它里面持有一個MessageQueue,然后這個循環(huán)不斷的從MessageQueue里拿Message出來執(zhí)行。如果MessageQueue里沒有東西執(zhí)行線程就會等待,那誰往MessageQueue里塞Message呢?沒錯,就是Handler。
如果你找這個文章,只是為了某種原因救急,那么到這里我覺得就結(jié)束了。但如果你還需要完全掌握這些概念并且能講給別人聽,那么下面部分應(yīng)該不會讓你失望呦 :)
這些對象之間的關(guān)系
大多數(shù)場景下,我們可能并不需要自己創(chuàng)建Looper,我們關(guān)注這些源自于對Handler的使用有疑惑。我們從一個常見的場景來進入主題。
從工作線程讓一段代碼在主線程中執(zhí)行
完成這個任務(wù)一般我們有兩種做法:
- Handler.post
直接上實現(xiàn)代碼:
Handler mainHandler = new Handler(Looper.getMainLooper()); //等同于context.getMainLooper()
Runnable myRunnable = new Runnable() {
@Override
public void run() {....} // This is your code
};
mainHandler.post(myRunnable);
代碼創(chuàng)建了一個使用主線程Looper的Handler對象,并把Runnable post到主線程Looper的MessageQueue里。如上面介紹所說,等Looper拿到這個runnable就會執(zhí)行。
- Activity.runOnUiThread
在你可以方便獲得Activity對象的時候,可以直接調(diào)用這個方法來實現(xiàn)代碼在主線程調(diào)用。我們來看看這個方法代碼實現(xiàn):
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
代碼實現(xiàn)里判斷了當(dāng)前線程是否是主線程,如果是主線程,直接執(zhí)行Runnable的run方法(action.run());如果不是,則使用綁定在主線程的handler把Runnable post到主線程Looper的MessageQueue里。
從這段代碼能思考到下面幾點:
- 調(diào)用runOnUiThread的時候,你可以從任何線程,只要你能獲得activity實例,不需要做任何線程判斷,因為系統(tǒng)幫你做了。
- 這個方法實現(xiàn)其實是封裝了一下方法一的Handler實現(xiàn)方式。
對象關(guān)系解析
先看類圖:

如上圖,Handler和Looper,Looper和MessageQueue,Thread之間都是組合關(guān)系。從Looper的官方文檔的推薦代碼我們看到Looper是如何給一個Thread添加消息隊列能力的:
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
調(diào)用了Looper.prepare()和Looper.loop(); 其中Looper.prepare是構(gòu)造Looper對象,并且創(chuàng)建MessageQueue和獲得Looper所在線程;Looper.loop()就是啟動死循環(huán),并從MessageQueue中讀取Message執(zhí)行(大家可以直接參考源碼來看,非常簡單)
Handler對象的創(chuàng)建成功的必要條件是Handler所在的線程擁有一個調(diào)用過prepare()的Looper,不然會拋出異常。然后Handler.post等方法調(diào)用,實際上是調(diào)用Handler擁有的Looper的MessageQueue.enqueue方法把要執(zhí)行的Runnable或者Message加入隊列中等待執(zhí)行。
它們之間的關(guān)系就是這么簡單,no big deal :D
為什么會這么設(shè)計?
根據(jù)上面的解析可以看出,沒有Looper的設(shè)計,Handler也沒有必要存在,那為什么需要Looper?
其實任何牽扯GUI的系統(tǒng),都會有這樣的需求,而且基本上是一個模式。Looper就做了兩件事:
- 把一個run()方法執(zhí)行完就結(jié)束的普通線程,轉(zhuǎn)化為一個可以持續(xù)執(zhí)行的Android app。
- 提供一個MessageQueue,GUI需要任務(wù)隊列來完成操作。
我們知道,當(dāng)程序開始運行,會執(zhí)行main方法,Android程序一般來說執(zhí)行在main方法所在的線程中——“主線程”,主線程也不是什么特別的線程,也是使用new Thread()方法來創(chuàng)建的,看下面的代碼:
public class HelloRunnable implements Runnable {
public void run() {
System.out.println("Hello from a thread!");
}
public static void main(String args[]) {
(new Thread(new HelloRunnable())).start();
}
}
不過,這跟我們所知道的Android主線程不同的是,run()方法執(zhí)行完,程序就退出了。在Android中,這顯然不是我們需要的,我們希望主程序初始化完成后,等待用戶的操作,反饋用戶的操作并執(zhí)行接下來的任務(wù)。并且用戶可能進行一系列連續(xù)的操作,我們需要一個先進先出的隊列來緩沖用戶操作并交給程序執(zhí)行,這就是Looper的作用,在任何GUI系統(tǒng)里,都有一樣的機制來完成這個目的。
而且Looper的官網(wǎng)說:
"Threads by default do not have a message loop associated with them", and Looper is a class "used to run a message loop for a thread".
這樣就更好理解Looper了。
為什么證明我們的描述,我們?nèi)?a target="_blank" rel="nofollow">ActivityThread class看它的main方法,把一個普通Thread變成了主線程:
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"));
}
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
好了,大概就是這樣了先,有問題一起探討~ 終于把這個寫了,居然寫了一天...