一辈赋、概念
Android的消息機制主要是指Handler的運行機制以及Handler所附帶的MessageQueue和Looper的工作過程。Handler的主要作用是將一個任務切換到某個指定的線程中去執(zhí)行膏燕。
Android中規(guī)定訪問UI只能在主線程中進行钥屈,如果在子線程中訪問UI,那么程序就會拋出異常坝辫。為什么系統不允許在子線程中訪問UI篷就?因為如果在多線程中并發(fā)訪問可能會導致UI控件處于不可預期的狀態(tài),而如果加上鎖機制近忙,首先會讓UI訪問的邏輯變得復雜竭业,其次會降低UI訪問的效率,因此最簡單且高效的方法就是采用單線程模型來處理UI操作及舍,對于開發(fā)者來說也不是很麻煩永品,只是需要通過Handler切換一下UI訪問的執(zhí)行線程即可。
二击纬、工作流程
Handler創(chuàng)建時會采用當前線程的Looper來構建內部的消息循環(huán)系統,如果當前線程沒有Looper钾麸,那么需要為當前線程創(chuàng)建Looper更振。Handler創(chuàng)建完畢后炕桨,其內部的Looper以及MessageQueue就可以和Handler一起協同工作了,然后通過Handler的post方法將一個Runnable投遞到Handler內部的Looper中去處理肯腕,也可以通過Handler的send方法發(fā)送一個消息献宫,這個消息同樣會在Looper中去處理。其實post方法最終也是通過send方法來完成的实撒。當Handler的send方法被調用時姊途,它會調用MessageQueue的enqueueMessage方法將這個消息放入消息隊列中,然后Looper發(fā)現有新消息到來時知态,就會處理這個消息捷兰,最終消息中的Runnable或者Handler的handleMessage方法就會被調用。注意负敏,Looper是運行在創(chuàng)建Handler所在的線程中的贡茅,這樣一來Handler中的業(yè)務邏輯就被切換到創(chuàng)建Handler所在的線程中去執(zhí)行了。
三其做、工作原理
1.ThreadLocal的工作原理
ThreadLocal是一個線程內部的數據存儲類顶考,通過它可以在指定的線程中存儲數據,數據存儲以后妖泄,只有在指定線程中可以獲取到存儲的數據驹沿,對于其他線程來說則無法獲取到數據。一般來說蹈胡,當某些數據是以線程為作用域并且不同線程具有不同的數據副本的時候渊季,就可以采用ThreadLocal。比如對于Handler來說审残,它需要獲取當前線程的Looper梭域,很顯然Looper的作用域就是線程并且不同線程具有不同的Looper,這個時候通過ThreadLocal就可以輕松實現Looper在線程中的存取搅轿。
ThreadLocal使用方法如下:
ThreadLocal<Boolean> mThreadLocal = new ThreadLocal<>();
private void testThreadLocal() {
mThreadLocal.set(true);
Log.d(TAG, "zwm, Thread#main, mThreadLocal: " + mThreadLocal.get()); //true
new Thread("Thread#1") {
@Override
public void run() {
mThreadLocal.set(false);
Log.d(TAG, "zwm, Thread#1, mThreadLocal: " + mThreadLocal.get()); //false
}
}.start();
new Thread("Thread#2") {
@Override
public void run() {
Log.d(TAG, "zwm, Thread#2, mThreadLocal: " + mThreadLocal.get()); //null
}
}.start();
}
2.消息隊列的工作原理
消息隊列在Android中指的是MessageQueue病涨,主要包含兩個操作,插入(enqueueMessage)和讀取(next)璧坟。enqueueMessage的作用是往消息隊列中插入一條消息既穆,而next的作用是從消息隊列中取出一條消息并將其從消息隊列中移除,next方法是一個無限循環(huán)的方法雀鹃,如果消息隊列中沒有消息幻工,那么next方法會一直阻塞。盡管MessageQueue叫消息隊列黎茎,但是它的內部實現并不是用隊列囊颅,而是通過一個單鏈表的數據結構來維護消息列表,單鏈表在插入和刪除上比較有優(yōu)勢。
3.Looper的工作原理
Looper在Android的消息機制中扮演著消息循環(huán)的角色踢代,具體來說就是它會不停地從MessageQueue中查看是否有新消息盲憎,如果有新消息就會立刻處理,否則就一直阻塞胳挎。
在子線程中創(chuàng)建Looper方法如下:
new Thread("Thread#2") {
@Override
public void run() {
Looper.prepare();
Handler handler = new Handler();
Looper.loop();
}
}.start();
Looper常用方法:
prepare:為當前線程創(chuàng)建一個Looper饼疙。
loop:開啟消息循環(huán)。
prepareMainLooper:給主線程也就是ActivityThread創(chuàng)建Looper慕爬。
getMainLooper:可以在任何地方獲取到主線程的Looper窑眯。
quit:直接退出Looper。
quitSafely:設定一個退出標記医窿,然后把消息隊列中的已有消息處理完畢后才安全退出磅甩。
Looper的loop方法是一個死循環(huán),它會調用MessageQueue的next方法來獲取新消息留搔,而next是一個阻塞操作更胖,當沒有消息時,next方法會一直阻塞隔显,這也會導致loop方法一直阻塞却妨。如果MessageQueue的next方法返回了新消息,Looper就會處理這條消息括眠,調用msg.target.dispatchMessage(msg)彪标,這里的msg.target就是發(fā)送這條消息的Handler對象,這樣Handler發(fā)送的消息最終又交給它的dispatchMessage方法來處理了掷豺,但是這里不同的是捞烟,Handler的dispatchMessage方法是在創(chuàng)建Handler時所使用的Looper中執(zhí)行的,這樣就成功地將代碼邏輯切換到指定的線程中去執(zhí)行了当船。
當Looper的quit方法被調用時题画,Looper就會調用MessageQueue的quit或者quitSafely方法來通知消息隊列退出,當消息隊列被標記為退出狀態(tài)時德频,它的next方法就會返回null苍息,這時Looper的loop方法就會跳出死循環(huán),即Looper退出壹置。Looper退出后竞思,通過Handler發(fā)送的消息會失敗,這個時候Handler的send方法會返回false钞护。在子線程中盖喷,如果手動為其創(chuàng)建了Looper,那么在所有的事情完成以后應該調用quit方法來終止消息循環(huán)难咕,否則這個子線程就會一直處于等待的狀態(tài)课梳,而如果退出Looper以后距辆,這個線程就會立刻終止,因此建議在不需要的時候終止Looper惦界。
4.Handler的工作原理
Handler的工作主要包含消息的發(fā)送和接收過程挑格。消息的發(fā)送可以通過post的一系列方法以及send的一系列方法來實現,post的一系列方法最終是通過send的一系列方法來實現的沾歪。Handler發(fā)送消息的過程僅僅是向消息隊列中插入了一條消息,MessageQueue的next方法就會返回這條消息給Looper雾消,Looper收到消息后就開始處理了灾搏,最終消息由Looper交由Handler處理,即Handler的dispatchMessage方法會被調用立润,這時Handler就進入了處理消息的階段狂窑。
public void dispatchMessage(Message msg) {
if(msg.callback != null) { //Runnable對象,實際上就是Handler的post方法所傳遞的Runnable參數
handleCallback(msg);
} else {
if(mCallback != null) { //Callback接口桑腮,創(chuàng)建Handler所傳遞的參數泉哈,Handler handler = new Handler(callback)
if(mCallback.handleMessage(msg)) { //Callback的handleMessage方法
return;
}
}
handleMessage(msg); //Handler的handleMessage方法
}
}
Handler的常用構造方法如下:
public Handler() //使用當前線程的Looper
public Handler(Looper looper) //使用特定的Looper
public Handler(Callback callback) //使用Callback接口,不需要派生Handler的子類
//在日常開發(fā)中破讨,創(chuàng)建Handler最常見的方式就是派生一個Handler的子類并重寫其handleMessage方法來處理具體的消息丛晦,
//而Callback給我們提供了另外一種使用Handler的方式,當我們不想派生子類時提陶,就可以通過Callback來實現烫沙。
四、主線程的消息循環(huán)
Android的主線程就是ActivityThread隙笆,主線程的入口方法為main锌蓄,在main方法中系統會通過Looper.prepareMainLooper()來創(chuàng)建主線程的Looper以及MessageQueue,并通過Looper.loop()來開啟主線程的消息循環(huán)撑柔。
主線程的消息循環(huán)開始了以后瘸爽,ActivityThread還需要一個Handler來和消息隊列進行交互,這個Handler就是ActivityThread.H铅忿,它內部定義了一組消息類型剪决,主要包含了四大組件的啟動和停止等過程。
ActivityThread通過ApplicationThread和AMS進行進程間通信辆沦,AMS以進程間通信的方式完成ActivityThread的請求后會回調ApplicationThread中的Binder方法昼捍,然后ApplicationThread會向H發(fā)送消息,H收到消息后會將ApplicationThread中的邏輯切換到ActivityThread中去執(zhí)行肢扯,即切換到主線程中去執(zhí)行妒茬,這個過程就是主線程的消息循環(huán)模型。