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

Android的消息機制指的是Handler的運行機制,本篇將總結Handler機制的相關知識點:

  • 消息機制概述
  • 消息機制分析

1.消息機制概述html

a.做用:跨線程通訊android

b.經常使用場景:當子線程中進行耗時操做後須要更新UI時,經過Handler將有關UI的操做切換到主線程中執行。安全

系統不建議在子線程訪問UI的緣由:UI控件非線程安全,在多線程中併發訪問可能會致使UI控件處於不可預期的狀態。而不對UI控件的訪問加上鎖機制的緣由有:bash

  • 上鎖會讓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.實現方法:ide

  • 在主線程實例化一個全局的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,考考本身吧!


但願這篇文章對你有幫助~

相關文章
相關標籤/搜索