Android Handler源碼 Hanlder的使用

Hanlder的使用

handler有兩種使用方式,下面開始介紹

方式一:使用Handler.sendMessage()

在該方式中,又可以分爲兩種:新建Handler子類(內部類)、匿名 Handler子類。

/**
* 方式一:新建Handler子類(內部類)
*/

// 步驟1:自定義Handler子類(繼承Handler類),複寫handleMessage()方法
    class mHandler extends Handler {
        // 通過複寫handlerMessage() 從而確定更新UI的操作
        @Override
        public void handleMessage(Message msg) {
         ...// 需執行的UI操作 
        }
    }

// 步驟2:在主線程中創建Handler實例
      private Handler mhandler = new mHandler();

// 步驟3:創建所需的消息對象
      Message msg = Message.obtain(); // 實例化消息對象
      msg.what = 1; // 消息標識
      msg.obj = "AA"; // 消息內容存放

 // 步驟4:在工作線程中 通過Handler發送消息到消息隊列中
 // 可通過sendMessage() / post()
 // 多線程可採用AsyncTask、繼承Thread類、實現Runnable
      mHandler.sendMessage(msg);
            mHandler.sendEmptyMessage(0); // 發送空消息
        mHandler.sendMessageDelayed(message,1000); //延遲1000ms後發送攜帶數據的消息

/** 
  * 方式2:匿名內部類
  */

// 步驟1:在主線程中 通過匿名內部類 創建Handler類對象
        private Handler mhandler = new  Handler(){
              // 通過複寫handlerMessage()從而確定更新UI的操作
             @Override
              public void handleMessage(Message msg) {
                        ...// 需執行的UI操作
                 }
          };

// 步驟2:創建消息對象
    Message msg = Message.obtain(); // 實例化消息對象
    msg.what = 1; // 消息標識
    msg.obj = "AA"; // 消息內容存放

// 步驟3:在工作線程中 通過Handler發送消息到消息隊列中
// 多線程可採用AsyncTask、繼承Thread類、實現Runnable
   mHandler.sendMessage(msg);
     mHandler.sendEmptyMessage(0); // 發送空消息
   mHandler.sendMessageDelayed(message,1000); //延遲1000ms後發送攜帶數據的消息

方式二:使用Handler.post()

// 步驟1:在主線程中創建Handler實例
    private Handler mhandler = new mHandler();

// 步驟2:在工作線程中 發送消息到消息隊列中 & 指定操作UI內容
    // 需傳入1個Runnable對象
    mHandler.post(new Runnable() {
            @Override
            public void run() {
                ... // 需執行的UI操作 
            }

    });

Hanlder源碼分析

Handler原理解析

主要內容

Handler的消息機制主要包含以下內容:

  • Message:消息
  • MessageQueue:消息隊列
  • Handler:消息管理類
  • Looper:消息循環類

Handler架構圖

Handler的架構圖如下:


創建Handler對象

具體使用

 private Handler mhandler = new  Handler(){
        // 通過複寫handlerMessage()從而確定更新UI的操作
        @Override
        public void handleMessage(Message msg) {
                ...// 需執行的UI操作
            }
    };

構造方法
Handler構造方法,調用了另一個構造方法,this(null, false)。

public Handler() {
    this(null, false);
}

幾點說明:

  • Handler需要綁定線程才能使用;綁定後,Handler的消息處理會在綁定的線程中執行。
  • 綁定方式:指定Looper對象,從而綁定了Looper對象所在的線程,因爲Looper對象本已綁定了對應線程。
  • 指定了Handler對象的Looper對象 = 綁定到了Looper對象所在的線程。

···
public Handler(Callback callback, boolean async) {

.....//省略代碼
//Looper.myLooper()作用:獲取當前線程的Looper對象;若線程無Looper對象則拋出異常
//即:若線程中無創建Looper對象,則也無法創建Handler對象
//故若需在子線程中創建Handler對象,則需先創建Looper對象
mLooper = Looper.myLooper();
if (mLooper == null) {
    throw new RuntimeException(
        "Can't create handler inside thread " + Thread.currentThread()
                + " that has not called Looper.prepare()");
}
// 綁定消息隊列對象(MessageQueue)
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;

//至此,保證了handler對象的MessageQueue關聯上Looper對象中MessageQueue

}
···

從上面可以看出:

  • 當創建Handler對象時,通過構造方法自動關聯當前線程的Looper對象和對應的消息隊列對象(MessageQueue),從而自動綁定了實現創建Handler對象操作的線程。

那麼問題來了,到目前爲止,我們不曾創建過Handler需要關聯的Looper對象和MessageQueue對象,那當前線程的Looper對象和對應的消息隊列的MessageQueue是什麼時候創建的呢?

創建循環對象Looper和消息隊列對象MessageQueue

  • 創建Looper對象的方法:Looper.prepareMainLooper()和Looper.prepare()。
  • Looper.prepareMainLooper():爲主線程(UI線程)創建一個Looper對象。
  • Looper.prepare()爲當前線程創建一個Looper對象

源碼分析1:Looper.prepareMainLooper()

在Android應用進程啓動時,會默認創建1個主線程ActivityThread,也叫UI線程,創建時,會自動調用ActivityThread的靜態的main()方法 = 應用程序的入口,main()內則會調用Looper.prepareMainLooper()爲主線程生成1個Looper對象,同時生成一個消息隊列對象MessageQueue。源碼如下:

public static void main(String[] args) {
   ../// 省略代碼
   // 1
    Looper.prepareMainLooper();
      ..///省略代碼
      // 創建主線程
    ActivityThread thread = new ActivityThread();
    thread.attach(false, startSeq);
    ..///省略代碼
    // 開啓消息循環,後面分析
    Looper.loop();
}

// 1處爲主線程創建1個Looper對象,同時生成1個消息隊列對象MessageQueue,繼續看prepareMainLooper(),

public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        // 設置sMainLooper,sMainLooper是主線程的Looper,可以通過getMainLooper()獲取
        sMainLooper = myLooper();
    }
}

可以看到,Looper.prepareMainLooper()內部其實是調用了 prepare()方法的。

源碼分析2:prepare()方法

prepare()的作用是爲當前線程創建1個循環器對象(Looper),同時也生成了1個消息隊列對象(MessageQueue)。
注意,如果要在子線程中創建Looper對象,則需在子線程中手動調用該方法。

private static void prepare(boolean quitAllowed) { 

        // 判斷sThreadLocal是否爲null,否則拋出異常
        // Looper.prepare()方法不能被調用兩次 = 1個線程中只能對應1個Looper實例
        // sThreadLocal = 1個ThreadLocal對象,用於存儲線程的變量
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    
    // 若爲初次Looper.prepare(),則創建Looper對象 & 存放在ThreadLocal變量中
    // Looper對象是存放在Thread線程裏的
    sThreadLocal.set(new Looper(quitAllowed));
}

這裏需要說明一下,是用ThreadLocal保存Looper對象。- Handler通過綁定Looper對象的方式綁定到當前線程。

  • 一個線程對應着一個Looper。
  • 一個Looper對應着一個MessageQueue。
  • 線程默認是沒有Looper的,線程需要通過Looper.prepare()、綁定Handler到Looper對象、Looper.loop()來建立消息循環。
  • 主線程(UI線程),也就是ActivityThread,在被創建的時候就會初始化Looper,所以主線程中可以默認使用Handler。

綜上所述:Threadlocal是一個線程內部的存儲類,可以在指定線程內存儲數據,數據存儲以後,只有指定線程可以得到存儲數據。ThreadLocal的作用是保證一個線程對應一個Looper,同時各個線程之間的Looper互不干擾。那麼ThreadLocal是怎麼保證的呢?

實際上ThreadLocal通過爲每個線程提供一個獨立的變量副本解決了變量併發訪問的衝突問題,ThreadLocal爲解決多線程程序的併發問題提供了一種新的思路。這裏再多提一下,ThreadLocal和Synchronized都是爲了解決多線程中相同變量的訪問衝突問題,不同的是,

  • Synchronized是通過線程等待,犧牲時間來解決訪問衝突
  • ThreadLocal是通過每個線程單獨一份存儲空間,犧牲空間來解決衝突

源碼分析3:Looper的構造方法

以上在prepare()方法中,創建Looper對象並存放在sThreadLocal中,下面查看Looper的構造方法。

private Looper(boolean quitAllowed) {
        
        // 創建1個消息隊列對象(MessageQueue)
        // 即 當創建1個Looper實例時,會自動創建一個與之配對的消息隊列對象(MessageQueue)
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

可以看到,在創建Looper對象的時候,創建了MessageQueue對象。
即一個Looper對應着一個MessageQueue。
==根據以上總結==

  • 創建主線程時,會自動調用ActivityThread的1個靜態的main();而main()內則會調用Looper.prepareMainLooper()爲主線程生成1個Looper對象,同時也會生成其對應的MessageQueue對象。

    1. 即 主線程的Looper對象自動生成,不需手動生成;而子線程的Looper對象則需手動通過Looper.prepare()創建。
    2. 在子線程若不手動創建Looper對象 則無法生成Handler對象。
  • 根據Handler的作用(在主線程更新UI),故Handler實例的創建場景 主要在主線程

  • 生成Looper & MessageQueue對象後,則會自動進入消息循環:Looper.loop()。

消息循環Looper.loop()

Looper.loop()主要是消息循環,從消息隊列中獲取消息,分發消息到Handler中。

public static void loop() {

        // ---  1.獲取當前Looper的消息隊列MessageQueue -----
        
        // 第一步
        // 獲取當前Looper對象
    final Looper me = myLooper();
    // myLooper()的作用是返回sThreadLocal存儲的Looper實例
    // 若me爲null,則拋出異常
    // 所以在執行loop()方法之前,必須執行prepare()方法,prepare()            //的作用是創建Looper實例
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    
    // 第二步
    // 獲取Looper實例中的消息隊列對象MessageQueue
    final MessageQueue queue = me.mQueue;

   // ......代碼省略
   
        //------ 2. 消息循環,無限循環 --------------
        
        // 第三步
    for (;;) {
            // 從MessageQueue中取出消息
            // 第四步
        Message msg = queue.next(); // might block
        // 如果消息爲空,則退出循環
        if (msg == null) {
        // No message indicates that the message queue is quitting.
            return;
        }
  // ......代碼省略
        final long dispatchEnd;
        try {
        
                // 第五步
                // 分發消息到對應的Handler
                // 把消息派發到msg的target屬性
                //target屬性實際上是一個handler對象
            msg.target.dispatchMessage(msg);
            
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        
        // ......代碼省略

      // 第六步
            // 回收消息
        msg.recycleUnchecked();
    }
}

消息循環Looper.loop(),主要作用的取出消息,通過以上代碼分析,大致分爲六步:

  • 第一步,獲取Looper對象
  • 第二步,獲取Looper實例中的消息隊列對象MessageQueue
  • 第三步,while()無限循環遍歷
  • 第四步,通過queue.next()從MessageQueue中取出一個Message對象
  • 第五步,通過msg.target.dispatchMessage(msg)來處理消息
  • 第六步,通過 msg.recycleUnchecked()來回收消息

其中第四,五,六三步涉及到消息的取出和消息的處理,在後面介紹。

消息的發送、取出和處理

對於消息的發送,取出和處理,參照下面的文章。

消息發送

消息的取出和處理

總結

最後再總結一下,Handler工作的流程圖:


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