Handler機制

從接觸Handler到現在接近4年,慢慢深入 慢慢摸索,直到今天看完何紅輝的源碼講解之後終於知道具體

最初瞭解到handler是因爲幾個異常
第一個是 子線程不能操控UI線程的控件 需要用handler.post(Runnable) 將其推到主線程消息隊列中執行
第二個是 直接在子線程中new handler()時拋出 cann't create handler inside  thread that has not called Looper.prepare()  第一次知道有個Looper 這東西

好的 先上圖
aaaaa

各個部件職能說明:
Thread:線程對象 申請一個線程運行空間,獨立運行子線程
Handler :處理者 提供給外部的一個句柄,用來操控消息隊列 和循環器   並執行消息具體處理
MessageQueue: 消息隊列  用於存放消息 採用隊列先進先出原則 
Looper : 循環器  內部是一個死循環,循環讀取MessageQueue中的消息,如果消息隊列爲空 那麼阻塞

解釋一下圖:
1.線程中有保留handler對象的引用
2.handler中保留looper和MessageQueue的引用
3.Looper中保留MessageQueue的引用

保留引用的作用就是可以調用 

而這個機制很重要的一點就是 :
Thread中的Handler對象被公開了, 也就是說在外部可以獲取此線程的handler, 那麼 就可以操控此線程的Looper和MessageQueue

有這個基礎 我們講下 消息處理過程(sendMessage,handleMessage過程 )
1. 首先由handler向MessageQueue中推送一條消息
2. 然後Looper阻塞的線程恢復運行,讀取MessageQueue中的消息,將消息發給Handler (這裏有個問題 Looper並沒有handler的引用 ,如何將消息發給handler)繼續等待/處理下一條消息
3. Handler調用我們自定義的處理方法處理消息


開始講代碼

一、首先看線程創建過程(以主線程爲例 因爲主線程中會創建Looper)

線程創建時主要做了這三件事 (主要討論handler相關)

Thread()
{
    Looper.prepare();                //在 線程中創建一個循環器looper
    handler=thread.getHandler();     //在線程中創建Handler()
    Looper.loop();                   //開始死循環
}


1.1 首先看Looper.prepare() 主要做了什麼
public static void prepare()
{
    //Looper中保留了線程的引用 即這裏的threadLocal
    if(threadLocal.get()!=null)
    {
        //判斷當前線程有沒有創建過Looper   get()方法就是獲取looper對象
        throw new RuntimeException("only one Looper may be created per thread");
    }
    threadLocal.set(new Looper);
}


很簡單, prepare函數只簡單地創建了一個Looper 

1.2 然後是getHandler()函數 
Handler 是 直接new出來的 我們看下它的構造函數
public Handler()
{
    mLooper=Looper.myLooper();         //Looper中提供靜態方法 myLooper 獲取當前looper
    if(mLooper==null)
    {
        throw new RuntimeException("Can't not create handler inside thread that has not called Looper.prepare");
    }
    mQueue=mLooper.mQueue;
    mCallback=null;
}


很明顯 handler中有 Looper 和MessageQueue的引用 mLooper 和 mQueue 在構造函數中初始化了這兩個引用  
然後中間的那個異常 就是說 必須要調用Looper.prepare()預先創建好Looper   否則 後面 從Looper中獲取mQueue 時肯定會報空指針異常
這裏主要就是創建handler 並初始化兩個引用


1.3  創建Thread的最後一步 : Looper.loop(); 執行循環
public static void loop()
{
    Looper me=myLooper();  //myLooper函數 同上
    if(me==null)
    {
        throw new RuntimeException("No Looper ; Looper.prepare wasn't called on this thread");
    }
    MessageQueue queue=me.mQueue;   //同上 獲取消息隊列對象
    while(true) //死循環 讀取消息
    {
       Message msg= queue.next();  //獲取 下一條消息  如果沒有就阻塞
        //判斷消息是否爲空 
        if(msg!=null)
        {
            if(msg.target==null)
            {
                //target是發送消息的handler對象(handler 發送消息的同時 把自己也保存在了消息對象裏面), 如果發送這個msg的handler不存在了(被銷燬)這個消息就不處理了
                return ;
            }
            msg.target.dispatchMessage(msg);  //將消息轉發給handler處理 (這裏有點繞  handler把消息發過來  這裏又把消息發回 handler  有病吧……  呵呵 後面再解釋)
            msg.recycle();         //消息對象回收 
        }
    }
}



以上 便是建立線程時 初始化handler機制的三個步驟:
1 創建Looper 
2 創建Handler
3 啓動Looper;

是不是感覺少了什麼? 怎麼沒找到MessageQueue創建在哪?
    其實MessageQueue是包含在Looper裏面的 創建Looper的同時就創建了MessageQueue 上面獲取MessageQueue 都是通過Looper獲取的

其實到現在 大部分流程都已經清楚了  那麼  開始解釋上面的 繞吧!
不懂得地方就是這句 msg.target.dispatchMessage(msg); 
我們先看下Message對象裏面到底有什麼東西
Message
{
   Handler target;           <pre name="code" class="java"><pre name="code" class="java">    //target是handler類型 其實也就是發送此消息的那個handler <pre name="code" class="java"><pre name="code" class="java">    //哈哈  <span style="color:#3333FF;">還記得上面的問題 Looper內沒有handler的引用 怎麼將消息發給handler麼?答案就在Message內 它保存了發送方的引用</span>
    //另外記得handler的一個方法麼:handler.obtainMessage().sendToTarget() 



Runnable callback; //Runnable 對象 想必是要運行什麼 先記着 Message next ; //下一條消息 消息隊列是鏈式存儲的 int arg1, arg2 ,what ; Object obj; ...............}



那麼 上面就是調用了handler的dispatchMessage方法
public void dispatchMessage(Message msg)
{
    //好的 這裏就用上了 上面的Runnble類型的Callback
    if(msg.callback !=null)
    {
        //當msg中的Callback不爲空時就調用msg中的Runnale callback
        <span style="color:#3333FF;">handleCallback(msg);</span>
    }else
    {
        if(mCallback!=null)
        {
            if(mCallback.handleMessage(msg)){
            {
                   return ;
            }
        }
        //如果Message沒自帶處理方法就調用 handler的handleMessage方法 這個方法我們一般會覆蓋它
        <span style="color:#3333FF;">handleMessage(msg);</span>
    }
}

private final void handleCallback(Message message)
{
    message.callback.run();
}
public void handleMessage(Message msg)
{
    //這個方法我們一般會覆蓋它 用它來處理我們發送的信息
}



到這裏 我們還是不清楚Runnable是在哪用到的 那麼 提示下 
我們使用handler有兩種方式:
(1) handler.sendMessage();
(2) handler.post(Runnable);
哈哈 再知道那個Runnable callback 哪裏來的吧  這兩種方式就是上面的if /else
其實這兩種方式是一樣的 都是通過sendMessageDelay()方法將Message對象發送到MessageQueue中 , sendMessage發送的是個包裝好的Message對象  而post方法是將Runnable封裝成了Message對象 


好的 還剩最後一個問題
handler將消息發給消息隊列   Looper取出來 又分給handler  有病吧!!! 還不如直接給handler處理……

我們分析下  
我們利用handler主要是爲了跨線程傳遞消息 ,
而這裏handler將消息發給消息隊列後 其實就是一個切換線程的過程 由原來的線程轉到了handler所在線程的內部looper循環  ,
如果直接交給handler處理就還是在原來的線程中操作。

另外通過消息隊列一轉換 由同步過程變成異步過程 
一是解耦 
二是 解放了handler  handler不必直接進行處理操作(如果這個操作很耗時 那麼多次調用handler可能會出現異常情況)

好了    handler機制到此結束 此過程部分爲主觀臆斷 如果有什麼不對 麻煩請給我指正 












發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章