1.消息機制概述html
a.做用:跨線程通訊。android
b.經常使用場景:當子線程中進行耗時操做後須要更新UI時,經過Handler將有關UI的操做切換到主線程中執行。安全
系統不建議在子線程訪問UI的緣由:UI控件非線程安全,在多線程中併發訪問可能會致使UI控件處於不可預期的狀態。而不對UI控件的訪問加上鎖機制的緣由有:bash
- 上鎖會讓UI控件變得複雜和低效
- 上鎖後會阻塞某些進程的執行
c.四要素:數據結構
Thread(線程):負責調度整個消息循環,即消息循環的執行場所。多線程
存在關係:併發
- 一個Thread只能有一個Looper,能夠有多個Handler;
- Looper有一個MessageQueue,能夠處理來自多個Handler的Message;
- MessageQueue有一組待處理的Message,這些Message可來自不一樣的Handler;
- Message中記錄了負責發送和處理消息的Handler;
- Handler中有Looper和MessageQueue;
圖片來源:android的消息處理機制之Looper,Handler,Message異步
d.實現方法:ide
實例:Service篇--異步消息處理機制函數
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.工做原理:
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詳解
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
複製代碼
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);
}
}
......
}
複製代碼
//第一種:send方式的Handler建立
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
//如UI操做
}
};
//第二種:post方式的Handler建立
Handler handler = new Handler();
複製代碼
注意:建立Handler實例以前必須先建立Looper實例,不然會拋RuntimeException。
對於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。
至此從源碼已走過一遍流程。
推薦閱讀:深刻了解Android的消息機制(源碼)
最後,將Handler機制彙總到一張圖:
如今,這裏有一些有關消息機制的Questions,考考本身吧!
但願這篇文章對你有幫助~