Android的訊息機制指的是Handler的執行機制,本篇將總結Handler機制的相關知識點:
- 訊息機制概述
- 訊息機制分析
1.訊息機制概述
a.作用:跨執行緒通訊。
b.常用場景:當子執行緒中進行耗時操作後需要更新UI時,通過Handler將有關UI的操作切換到主執行緒中執行。
系統不建議在子執行緒訪問UI的原因:UI控制元件非執行緒安全,在多執行緒中併發訪問可能會導致UI控制元件處於不可預期的狀態。而不對UI控制元件的訪問加上鎖機制的原因有:
- 上鎖會讓UI控制元件變得複雜和低效
- 上鎖後會阻塞某些程式的執行
c.四要素:
- Message(訊息):需要被傳遞的訊息,其中包含了訊息ID,訊息處理物件以及處理的資料等,由MessageQueue統一列隊,最終由Handler處理。
- MessageQueue(訊息佇列):用來存放Handler傳送過來的訊息,內部通過單連結串列的資料結構來維護訊息列表,等待Looper的抽取。
- Handler(處理者):負責Message的傳送及處理。
- Handler.sendMessage():向訊息池傳送各種訊息事件。
- Handler.handleMessage():處理相應的訊息事件。
- Looper(訊息泵):通過Looper.loop() 不斷地從MessageQueue中抽取Message,按分發機制將訊息分發給目標處理者。
Thread(執行緒):負責排程整個訊息迴圈,即訊息迴圈的執行場所。
存在關係:
- 一個Thread只能有一個Looper,可以有多個Handler;
- Looper有一個MessageQueue,可以處理來自多個Handler的Message;
- MessageQueue有一組待處理的Message,這些Message可來自不同的Handler;
- Message中記錄了負責傳送和處理訊息的Handler;
- Handler中有Looper和MessageQueue;
圖片來源:android的訊息處理機制之Looper,Handler,Message
d.實現方法:
- 在主執行緒例項化一個全域性的Handler物件;
- 在需要執行UI操作的子執行緒裡例項化一個Message並填充必要資料,呼叫Handler.sendMessage(Message msg)方法傳送出去;
- 複寫handleMessage()方法,對不同Message執行相關操作;
2.訊息機制分析
a.工作流程:
Handler.sendMessage()
傳送訊息時,會通過MessageQueue.enqueueMessage()
向MessageQueue中新增一條訊息;- 通過
Looper.loop()
開啟迴圈後,不斷輪詢呼叫MessageQueue.next()
; - 呼叫目標
Handler.dispatchMessage()
去傳遞訊息,目標Handler收到訊息後呼叫Handler.handlerMessage()
處理訊息。
簡單來看,即
Handler
將Message
傳送到Looper
的成員變數MessageQueue
中,之後Looper
不斷迴圈遍歷MessageQueue
從中讀取Message
,最終回撥給Handler
處理。如圖:
b.工作原理:
- (1)Looper的建立:先從應用程式的入口函式
ActivityThread.main()
看起,在這裡(主執行緒)系統自動建立了Looper,主要方法:
//主執行緒中不需要自己建立Looper
public static void main(String[] args) {
......
Looper.prepareMainLooper();//為主執行緒建立Looper,該方法內部又呼叫 Looper.prepare()
......
Looper.loop();//開啟訊息輪詢
......
}
複製程式碼
注意:
- 子執行緒的Looper需要手動去建立,標準寫法是:
//子執行緒中需要自己建立一個Looper
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();//為子執行緒建立Looper
Looper.loop(); //開啟訊息輪詢
}
}).start();
複製程式碼
- 無論是主執行緒還是子執行緒,Looper只能被建立一次,即一個Thread只有一個Looper。
- 所建立的Looper會儲存在ThreadLocal(執行緒本地儲存區)中,它不是執行緒,作用是幫助Handler獲得當前執行緒的Looper。更多講解見ThreadLocal詳解
- (2)MessageQueue的建立:在Looper的建構函式建立了一個MessageQueue:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
複製程式碼
- (3)Message輪詢及處理:有了Looper和MessageQueue之後通過
Looper.loop()
開啟訊息輪詢:
public static void loop() {
......
for (;;) {//死迴圈
Message msg = queue.next(); //用於提取下一條資訊,該方法裡同樣有個for(;;)死迴圈,當沒有可處理該Message的Handler時,會一直阻塞
if (msg == null) {
return;
}
......
try {
msg.target.dispatchMessage(msg);//如果從MessageQueue中拿到Message,由和它繫結的Handler(msg.target)將它傳送到MessageQueue
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
......
}
複製程式碼
- 現在就剩建立Handler及Message傳送了。(4)先看Handler的建立:有兩種形式的Handler:
//第一種:send方式的Handler建立
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
//如UI操作
}
};
//第二種:post方式的Handler建立
Handler handler = new Handler();
複製程式碼
注意:建立Handler例項之前必須先建立Looper例項,否則會拋RuntimeException。
- (5) Message的傳送:
對於send方式的Handler:建立好一個Message後,呼叫Handler的以下幾種常見的方法來傳送訊息:
sendEmptyMessage(); //傳送空訊息
sendEmptyMessageAtTime(); //傳送按照指定時間處理的空訊息
sendEmptyMessageDelayed(); //傳送延遲指定時間處理的空訊息
sendMessage(); //傳送一條訊息
sendMessageAtTime(); //傳送按照指定時間處理的訊息
sendMessageDelayed(); //傳送延遲指定時間處理的訊息
sendMessageAtFrontOfQueue(); //將訊息傳送到訊息隊頭
複製程式碼
對於post方式的Handler,可在子執行緒直接呼叫Handler的以下幾種常見方法,使得切換到主執行緒:
post(Runnable r)
postAtFrontOfQueue(Runnable r)
postAtTime(Runnable r, Object token, long uptimeMillis)
postAtTime(Runnable r, long uptimeMillis)
postDelayed(Runnable r, long delayMillis)
//例如,postDelayed()方法
handler.postDelayed(new Runnable() {
@Override
public void run() {
//如UI操作
}
},300);
複製程式碼
通過以上各種Handler的傳送方法,都會依次呼叫 Handler.sendMessageDelayed
->Handler.sendMessageAtTime()
->Handler.enqueueMessage()
最終將Message傳送到MessageQueue。
至此從原始碼已走過一遍流程。
最後,將Handler機制彙總到一張圖:
現在,這裡有一些有關訊息機制的Questions,考考自己吧!
希望這篇文章對你有幫助~