Android 消息機(jī)制詳解及實(shí)例代碼
Android 消息機(jī)制
1.概述
Android應(yīng)用啟動(dòng)時(shí),會(huì)默認(rèn)有一個(gè)主線程(UI線程),在這個(gè)線程中會(huì)關(guān)聯(lián)一個(gè)消息隊(duì)列(MessageQueue),所有的操作都會(huì)被封裝成消息隊(duì)列然后交給主線程處理。為了保證主線程不會(huì)退出,會(huì)將消息隊(duì)列的操作放在一個(gè)死循環(huán)中,程序就相當(dāng)于一直執(zhí)行死循環(huán),每循環(huán)一次,從其內(nèi)部的消息隊(duì)列中取出一個(gè)消息,然后回調(diào)相應(yīng)的消息處理函數(shù)(handlerMessage),執(zhí)行完成一個(gè)消息后則繼續(xù)循環(huán),若消息隊(duì)列為空,線程則會(huì)阻塞等待。因此不會(huì)退出。如下圖所示:

Handler 、 Looper 、Message有啥關(guān)系?
在子線程中完成耗時(shí)操作,很多情況下需要更新UI,最常用的就是通過(guò)Handler將一個(gè)消息Post到UI線程中,然后再在Handler的handlerMessage方法中進(jìn)行處理。而每個(gè)Handler都會(huì)關(guān)聯(lián)一個(gè)消息隊(duì)列(MessageQueue),Looper負(fù)責(zé)的就是創(chuàng)建一個(gè)MessageQueue,而每個(gè)Looper又會(huì)關(guān)聯(lián)一個(gè)線程(Looper通過(guò)ThreadLocal封裝)。默認(rèn)情況下,MessageQueue只有一個(gè),即主線程的消息隊(duì)列。
上面就是Android消息機(jī)制的基本原理,如果想了解更詳細(xì),我們從源碼開始看。
2.源碼解讀
(1)ActivityThread主線程中啟動(dòng)啟動(dòng)消息循環(huán)Looper
public final class ActivityThread {
public static void main(String[] args) {
//代碼省略
//1.創(chuàng)建消息循環(huán)的Looper
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
AsyncTask.init();
//2.執(zhí)行消息循環(huán)
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
}
ActivityThread通過(guò)Looper.prepareMainLooper()創(chuàng)建主線程的消息隊(duì)列,最后執(zhí)行Looper.loop()來(lái)啟動(dòng)消息隊(duì)列。Handler關(guān)聯(lián)消息隊(duì)列和線程。
(2)Handler關(guān)聯(lián)消息隊(duì)列和線程
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()");
}
//獲取消息隊(duì)列
mQueue = mLooper.mQueue;
}
Handler會(huì)在內(nèi)部通過(guò)Looper.getLooper()方法來(lái)獲取Looper對(duì)象,并且與之關(guān)聯(lián),并獲取消息隊(duì)列。那么Looper.getLooper()如何工作的呢?
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
public static @NonNull MessageQueue myQueue() {
return myLooper().mQueue;
}
public static void prepare() {
prepare(true);
}
//為當(dāng)前線程設(shè)置一個(gè)Looper
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));
}
//設(shè)置UI線程的Looper
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
在Looper類中,myLooper()方法,通過(guò)sThreadLocal.get()來(lái)獲取的,在prepareMainLooper()中調(diào)用prepare()方法,在這個(gè)方法中創(chuàng)建了一個(gè)Looper對(duì)象,并將對(duì)象設(shè)置了sThreadLocal()。這樣隊(duì)列就和線程關(guān)聯(lián)起來(lái)了。通過(guò)sThreadLocal.get()方法,保證不同的線程不能訪問(wèn)對(duì)方的消息隊(duì)列。
為什么要更新UI的Handler必須在主線程中創(chuàng)建?
因?yàn)镠andler要與主線程的消息隊(duì)列關(guān)聯(lián)上,這樣handlerMessage才會(huì)執(zhí)行在UI線程,此時(shí)UI線程才是安全的。
(3)消息循環(huán),消息處理
消息循環(huán)的建立就是通過(guò)Looper.loop()方法。源代碼如下:
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//1.獲取消息隊(duì)列
final MessageQueue queue = me.mQueue;
//2.死循環(huán),即消息循環(huán)
for (;;) {
//3.獲取消息,可能阻塞
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
//4.處理消息
msg.target.dispatchMessage(msg);
//回收消息
msg.recycleUnchecked();
}
}
從上述程序我們可以看出,loop()方法的實(shí)質(zhì)上是建立一個(gè)死循環(huán),然后通過(guò)從消息隊(duì)列中逐個(gè)取出消息,最后處理消息。對(duì)于Looper:通過(guò)Looper.prepare()來(lái)創(chuàng)建Looper對(duì)象(消息隊(duì)列封裝在Looper對(duì)象中),并且保存在sThreadLocal中,然后通過(guò)通過(guò)Looper.loop()進(jìn)行消息循環(huán),這兩步通常成對(duì)出現(xiàn)。
public final class Message implements Parcelable {
//target處理
Handler target;
//Runnable類型的callback
Runnable callback;
//下一條消息,消息隊(duì)列是鏈?zhǔn)酱鎯?chǔ)的
Message next;
}
從源碼中可以看出,target是Handler類型。實(shí)際上就是轉(zhuǎn)了一圈,通過(guò)Handler發(fā)送消息給消息隊(duì)列,消息隊(duì)列又將消息分發(fā)給Handler處理。在Handle類中:
//消息處理函數(shù),子類覆寫
public void handleMessage(Message msg) {
}
private static void handleCallback(Message message) {
message.callback.run();
}
//分發(fā)消息
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
從上述程序可以看出,dispatchMessage只是一個(gè)分發(fā)的方法,如果Run nable類型的callback為空,則執(zhí)行handleMessage來(lái)處理消息,該方法為空,我們會(huì)將更新UI的代碼寫在該函數(shù)中;如果callback不為空,則執(zhí)行handleCallback來(lái)處理,該方法會(huì)調(diào)用callback的run方法。其實(shí)這是Handler分發(fā)的兩種類型,比如post(Runnable callback)則callback就不為空,當(dāng)我們使用Handler來(lái)sendMessage時(shí)通常不設(shè)置callback,因此,執(zhí)行handlerMessage。
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
public String getMessageName(Message message) {
if (message.callback != null) {
return message.callback.getClass().getName();
}
return "0x" + Integer.toHexString(message.what);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
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);
}
從上述程序可以看到,在post(Runnable r)時(shí),會(huì)將Runnable包裝成Message對(duì)象,并且將Runnable對(duì)象設(shè)置給Message對(duì)象的callback,最后會(huì)將該對(duì)象插入消息隊(duì)列。sendMessage也是類似實(shí)現(xiàn):
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
不管是post一個(gè)Runnable還是Message,都會(huì)調(diào)用sendMessageDelayed(msg, time)方法。Handler最終將消息追加到MessageQueue中,而Looper不斷地從MessageQueue中讀取消息,并且調(diào)用Handler的dispatchMessage分發(fā)消息,這樣消息就源源不斷地被產(chǎn)生、添加到MessageQueue、被Handler處理,Android應(yīng)用就運(yùn)轉(zhuǎn)起來(lái)了。
3.檢驗(yàn)
new Thread(){
Handler handler = null;
public void run () {
handler = new Handler();
};
}.start();
上述代碼有問(wèn)題嗎?
Looper對(duì)象是ThreadLocal的,即每個(gè)線程都用自己的Looper,這個(gè)Looper可以為空。但是,當(dāng)在子線程中創(chuàng)建Handler對(duì)象時(shí),如果Looper為空,那么會(huì)出現(xiàn)異常。
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()");
}
//獲取消息隊(duì)列
mQueue = mLooper.mQueue;
}
當(dāng)mLooper為空時(shí),拋出異常。這是因?yàn)長(zhǎng)ooper對(duì)象沒(méi)有創(chuàng)建,因此,sThreadLocal.get()會(huì)返回null。Handler的基本原理就是要與MessageQueue建立關(guān)聯(lián),并且將消息投遞給MessageQueue,如果沒(méi)有MessageQueue,則Handler沒(méi)有存在的必要,而MessageQueue又被封住在Looper中,因此創(chuàng)建Handler時(shí),Looper一定不能為空。解決辦法如下:
new Thread(){
Handler handler = null;
public void run () {
//為當(dāng)前線程創(chuàng)建Looper,并且綁定到ThreadLocal中
Looper.prepare()
handler = new Handler();
//啟動(dòng)消息循環(huán)
Looper.loop();
};
}.start();
如果只創(chuàng)建Looper不啟動(dòng)消息循環(huán),雖然不拋出異常,但是通過(guò)handler來(lái)post或者sendMessage()也不會(huì)有效。因?yàn)殡m然消息會(huì)被追加到消息隊(duì)列,但是并沒(méi)有啟動(dòng)消息循環(huán),也就不會(huì)從消息隊(duì)列中獲取消息并且執(zhí)行了。
感謝閱讀,希望能幫助到大家,謝謝大家對(duì)本站的支持!
相關(guān)文章
Android實(shí)現(xiàn)鍵盤彈出界面上移的實(shí)現(xiàn)思路
這篇文章主要介紹了Android實(shí)現(xiàn)鍵盤彈出界面上移的實(shí)現(xiàn)思路,需要的朋友可以參考下2018-04-04
Android實(shí)現(xiàn)紙飛機(jī)的簡(jiǎn)單操作
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)紙飛機(jī)的簡(jiǎn)單操作,類似于漂流瓶功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-05-05
Android 自定義閃屏頁(yè)廣告倒計(jì)時(shí)view效果
這篇文章主要介紹了Android 自定義閃屏頁(yè)廣告倒計(jì)時(shí)view效果,需要的朋友可以參考下2017-05-05
Android?App實(shí)現(xiàn)閃屏頁(yè)廣告圖的全屏顯示實(shí)例
這篇文章主要為大家介紹了Android?App實(shí)現(xiàn)閃屏頁(yè)廣告圖的全屏顯示實(shí)例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08
Android 拍照選擇圖片并上傳功能的實(shí)現(xiàn)思路(包含權(quán)限動(dòng)態(tài)獲取)
這篇文章主要介紹了Android 拍照(選擇圖片)并上傳(包含權(quán)限動(dòng)態(tài)獲取),本文分步驟給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-12-12
Android實(shí)現(xiàn)發(fā)送短信驗(yàn)證碼倒計(jì)時(shí)功能示例
本篇文章主要介紹了Android實(shí)現(xiàn)發(fā)送短信驗(yàn)證碼倒計(jì)時(shí)功能示例,這里整理了詳細(xì)的代碼,有需要的小伙伴可以參考下。2017-03-03
Android靜默安裝實(shí)現(xiàn)方案 仿360手機(jī)助手秒裝和智能安裝功能
這篇文章主要介紹了Android靜默安裝實(shí)現(xiàn)方案,仿360手機(jī)助手秒裝和智能安裝功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-11-11
Android初學(xué)者必須知道的10個(gè)技術(shù)
本篇內(nèi)容給大家整理10個(gè)作為Android初學(xué)者必須要了解和會(huì)用的技術(shù)以及詳細(xì)代碼分析,需要的朋友收藏下慢慢學(xué)習(xí)吧。2017-12-12

