要點提煉|開發藝術之訊息機制

釐米姑娘發表於2017-12-25

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執行相關操作;

例項:Service篇--非同步訊息處理機制


2.訊息機制分析

a.工作流程:

  • Handler.sendMessage()傳送訊息時,會通過MessageQueue.enqueueMessage()向MessageQueue中新增一條訊息;
  • 通過Looper.loop()開啟迴圈後,不斷輪詢呼叫MessageQueue.next()
  • 呼叫目標Handler.dispatchMessage()去傳遞訊息,目標Handler收到訊息後呼叫Handler.handlerMessage()處理訊息。

簡單來看,即HandlerMessage傳送到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。

至此從原始碼已走過一遍流程。

推薦閱讀:深入瞭解Android的訊息機制(原始碼)

最後,將Handler機制彙總到一張圖:

要點提煉|開發藝術之訊息機制

圖片來源android的訊息機制——Handler機制

現在,這裡有一些有關訊息機制的Questions,考考自己吧!


希望這篇文章對你有幫助~

相關文章