handler

 

畫圖說明:安全

 

我在學習和使用handler的時候,對與它相關的源代碼進行的研究,說到handler機制,就要設計到5個類(畫圖),網絡

Handler、MessageQueue、Looper、Thread、還有一個Message;ide

Message是消息,它由MessageQueue統一列隊,由Handler處理。函數

Handler是處理者,他負責發送和處理Message消息。oop

MessageQueue指消息隊列,它用來存放Handler發送過來的隊列,而且按照先入先出的規則執行。學習

Looper的做用就像抽水的水泵,它不斷的從MessageQueue中去抽取Message並執行。this

Thread線程,是消息循環的執行場所。spa

知道了這幾個類就能夠說說消息機制的原理了,在建立Activity以前,當系統啓動的時候,先加載ActivityThread這個類,在這個類的main函數中,調用Looper.prepareMainLooper()進行初始化Looper對象,而後建立主線程的handler對象,隨後才建立ActivityThread對象,最後調用Looper.loop()方法,不斷的進行輪詢消息隊列中的消息。也就是說,在ActivityThread和Activity建立以前,就已經開啓了Looper的loop()方法,不斷的進行輪詢消息。線程

咱們能夠畫圖來講明handler機制的原理:設計

咱們經過Message.obtain()準備消息數據以後,

第一步是使用sendMessage():經過Handler將消息發送給消息隊列

第二步、在發送消息的時候,使用message.target=this爲handler發送的message貼上當前handler的標籤

第三步、開啓HandlerThread線程,執行run方法。

四、在HandlerThread類的run方法中開啓輪詢器進行輪詢:調用Looper.loop()方法進行輪詢消息隊列的消息

五、在消息隊列MessageQueue中enqueueMessage(Message msg, long when)方法裏,對消息進行入列,即依據傳入的時間進行消息入列(排隊)

六、輪詢消息:與此同時,Looper在不斷的輪詢消息隊列

七、在Looper.loop()方法中,獲取到MessageQueue對象後,從中取出消息(Message msg = queue.next()),若是沒有消息會堵塞

八、分發消息:從消息隊列中取出消息後,調用msg.target.dispatchMessage(msg);進行分發消息

九、將處理好的消息分發給指定的handler處理,即調用了handler的dispatchMessage(msg)方法進行分發消息。

十、在建立handler時,複寫的handleMessage方法中進行消息的處理

十一、回收消息:在消息使用完畢後,在Looper.loop()方法中調用msg.recycle(),將消息進行回收,即將消息的全部字段恢復爲初始狀態。

 12. what帶字段,obj帶數據,   建立方法 new Message 或 Message.obtain()

 

 

handler機制?即handler的做用

在Android的UI開發中,咱們常常會使用Handler來控制主UI程序的界面變化。有關Handler的做用,

咱們總結爲:與其餘線程協同工做,接收其餘線程的消息並經過接收到的消息更新主UI線程的內容。

咱們假設在一個UI界面上面,有一個按鈕,當點擊這個按鈕的時候,會進行網絡鏈接,並把網絡上的一個字符串拿下來顯示到界面上的一個 TextView上面,這時就出現了一個問題,若是這個網絡鏈接的延遲過大,多是10秒鐘甚至更長,那咱們的界面將處於一直假死狀態,而若是這段時間超 過5秒鐘的話,程序會出現異常。

這時咱們會想到使用線程來完成以上工做,即當按鈕被按下的時候新開啓一個線程來完成網絡鏈接工做,並把獲得的結果更新到UI上面。可是,這時候又會 出現另外一個問題,在Android中,主線程是非線程安全的,也就是說UI的更新只能在本線程中完成,其餘線程沒法直接對主線程進行操做。

爲了解決以上問題,Android設計了Handler機制,由Handler來負責與子線程進行通信,從而讓子線程與主線程之間創建起協做的橋樑,使Android的UI更新的問題獲得完美的解決。接下來ATAAW.COM舉例來詮釋Handler的基本使用方法。

A、Handler的工做原理

通常狀況下,在主線程中咱們綁定了Handler,並在事件觸發上面建立新的線程用於完成某些耗時的操做,當子線程中的工做完成以後,會對Handler發送一個完成的信號,而Handler接收到信號後,就進行主UI界面的更新操做。

B、Handler與子線程協做實例

一、建立Handler實現類,在主UI所在類中的內部類

1     class MyHandler extends Handler {   

2     public MyHandler() {   

1        }   

1     public MyHandler(Looper L) {   

1     super(L);   

1        }   

1        // 重寫handleMessage方法,接受數據並更新UI   

1        @Override   

1     public void handleMessage(Message msg) {   

2     super.handleMessage(msg);   

1        //此處根據msg內容進行UI操做   

1            }   

1        } 

二、子線程的實現

1     class MyThread implements Runnable {   

1     public void run() {   

1                Message msg = new Message();   

1                Bundle b = new Bundle();  

1                b.putString("cmd", "update");   

1                msg.setData(b);   

1                MainActivity.this.myHandler.sendMessage(msg);//通知Handler更新UI   

2            }   

1        } 

經過以上的兩個實現,咱們只須要在MainActivity中聲明MyHandler實例對象就能夠完成線程之間的通信和界面的更新操做。

MyHandler myHandler = newMyHandler(); 

 

調用流程

  • Message類的obtain方法

    • 消息隊列順序的維護是使用單鏈表的形式來維護的
    • 把消息池裏的第一條數據取出來,而後把第二條變成第一條

      if (sPool != null) { Message m = sPool; sPool = m.next; m.next = null; sPoolSize--; return m; }

  • 建立Handler對象時,在構造方法中會獲取Looper和MessageQueue的對象

    public Handler() {
        ...
        //拿到looper
        mLooper = Looper.myLooper();
        ...
        //拿到消息隊列
        mQueue = mLooper.mQueue;
        mCallback = null;
    }
  • 查看myLooper方法體,發現Looper對象是經過ThreadLocal獲得的,在查找ThreadLocal的set方法時發現

    • Looper是直接new出來的,而且在Looper的構造方法中,new出了消息隊列對象

      sThreadLocal.set(new Looper());
      
      private Looper() {
          mQueue = new MessageQueue();
          mRun = true;
          mThread = Thread.currentThread();
      }
    • sThreadLocal.set(new Looper())是在Looper.prepare方法中調用的
  • prepare方法是在prepareMainLooper()方法中調用的

    public static final void prepareMainLooper() {
        prepare();
        ...
    }
  • 在應用啓動時,主線程要被啓動,ActivityThread會被建立,在此類的main方法中

    public static final void main(String[] args) {
        ...
        //建立Looper和MessageQueue
        Looper.prepareMainLooper();
        ...
        //輪詢器開始輪詢
        Looper.loop();
        ...
    }
  • Looper.loop()方法中有一個死循環

    while (true) {
        //取出消息隊列的消息,可能會阻塞
        Message msg = queue.next(); // might block
        ...
        //解析消息,分發消息
        msg.target.dispatchMessage(msg);
        ...
    }
  • Linux的一個進程間通訊機制:管道(pipe)。原理:在內存中有一個特殊的文件,這個文件有兩個句柄(引用),一個是讀取句柄,一個是寫入句柄

  • 主線程Looper從消息隊列讀取消息,當讀完全部消息時,進入睡眠,主線程阻塞。子線程往消息隊列發送消息,而且往管道文件寫數據,主線程即被喚醒,從管道文件讀取數據,主線程被喚醒只是爲了讀取消息,當消息讀取完畢,再次睡眠

  • Handler發送消息,sendMessage的全部重載,實際最終都調用了sendMessageAtTime

    public boolean sendMessageAtTime(Message msg, long uptimeMillis)
    {
       ...
        //把消息放到消息隊列中
        sent = queue.enqueueMessage(msg, uptimeMillis);
       ...
    }
  • enqueueMessage把消息經過從新排序放入消息隊列

    final boolean enqueueMessage(Message msg, long when) {
        ...
        final boolean needWake;
        synchronized (this) {
           ...
            //對消息的從新排序,經過判斷消息隊列裏是否有消息以及消息的時間對比
            msg.when = when;
    
            Message p = mMessages;
            //把放入消息隊列的消息置爲消息隊列第一條消息
            if (p == null || when == 0 || when < p.when) {
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked; // new head, might need to wake up
            } else {
                //判斷時間順序,爲剛放進來的消息尋找合適的位置
                Message prev = null;
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
                msg.next = prev.next;
                prev.next = msg;
                needWake = false; // still waiting on head, no need to wake up
            }
        }
        //喚醒主線程
        if (needWake) {
            nativeWake(mPtr);
        }
        return true;
    }
  • Looper.loop方法中,獲取消息,而後分發消息

    //獲取消息隊列的消息
     Message msg = queue.next(); // might block
     ...
    //分發消息,消息由哪一個handler對象建立,則由它分發,並由它的handlerMessage處理  
     msg.target.dispatchMessage(msg);
  • message對象的target屬性,用於記錄該消息由哪一個Handler建立,在obtain方法中賦值

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息