[Android 知識點剖析] Looper Handler MessageQueue

快速了解消息循環場景

首先一句話總結一下上面這些概念:

Looper是為一個Thread添加一個事件循環(Message Loop)
MessageQueue是Looper中管理Message的隊列
Message是事件循環中的事件對象
Handler是用來創建Message并且管理發送Message的

接下來用通俗的語言來描述一下這些對象運行的場景

Looper是一個死循環,它里面持有一個MessageQueue,然后這個循環不斷的從MessageQueue里拿Message出來執行。如果MessageQueue里沒有東西執行線程就會等待,那誰往MessageQueue里塞Message呢?沒錯,就是Handler。

如果你找這個文章,只是為了某種原因救急,那么到這里我覺得就結束了。但如果你還需要完全掌握這些概念并且能講給別人聽,那么下面部分應該不會讓你失望呦 :)

這些對象之間的關系

大多數場景下,我們可能并不需要自己創建Looper,我們關注這些源自于對Handler的使用有疑惑。我們從一個常見的場景來進入主題。

從工作線程讓一段代碼在主線程中執行

完成這個任務一般我們有兩種做法:

  • Handler.post
    直接上實現代碼:
Handler mainHandler = new Handler(Looper.getMainLooper()); //等同于context.getMainLooper()
Runnable myRunnable = new Runnable() {
    @Override 
    public void run() {....} // This is your code
};
mainHandler.post(myRunnable);

代碼創建了一個使用主線程Looper的Handler對象,并把Runnable post到主線程Looper的MessageQueue里。如上面介紹所說,等Looper拿到這個runnable就會執行。

  • Activity.runOnUiThread
    在你可以方便獲得Activity對象的時候,可以直接調用這個方法來實現代碼在主線程調用。我們來看看這個方法代碼實現:
public final void runOnUiThread(Runnable action) {
        if (Thread.currentThread() != mUiThread) {
            mHandler.post(action);
        } else {
            action.run();
        }
    }

代碼實現里判斷了當前線程是否是主線程,如果是主線程,直接執行Runnable的run方法(action.run());如果不是,則使用綁定在主線程的handler把Runnable post到主線程Looper的MessageQueue里。
從這段代碼能思考到下面幾點:

  1. 調用runOnUiThread的時候,你可以從任何線程,只要你能獲得activity實例,不需要做任何線程判斷,因為系統幫你做了。
  2. 這個方法實現其實是封裝了一下方法一的Handler實現方式。
對象關系解析

先看類圖:


類圖

如上圖,Handler和Looper,Looper和MessageQueue,Thread之間都是組合關系。從Looper的官方文檔的推薦代碼我們看到Looper是如何給一個Thread添加消息隊列能力的:

class LooperThread extends Thread {
      public Handler mHandler;
      public void run() {
          Looper.prepare();
          mHandler = new Handler() {
              public void handleMessage(Message msg) {
                  // process incoming messages here
              }
          };
          Looper.loop();
      }
  }

調用了Looper.prepare()和Looper.loop(); 其中Looper.prepare是構造Looper對象,并且創建MessageQueue和獲得Looper所在線程;Looper.loop()就是啟動死循環,并從MessageQueue中讀取Message執行(大家可以直接參考源碼來看,非常簡單)
Handler對象的創建成功的必要條件是Handler所在的線程擁有一個調用過prepare()的Looper,不然會拋出異常。然后Handler.post等方法調用,實際上是調用Handler擁有的Looper的MessageQueue.enqueue方法把要執行的Runnable或者Message加入隊列中等待執行。
它們之間的關系就是這么簡單,no big deal :D

為什么會這么設計?

根據上面的解析可以看出,沒有Looper的設計,Handler也沒有必要存在,那為什么需要Looper?
其實任何牽扯GUI的系統,都會有這樣的需求,而且基本上是一個模式。Looper就做了兩件事:

  1. 把一個run()方法執行完就結束的普通線程,轉化為一個可以持續執行的Android app。
  2. 提供一個MessageQueue,GUI需要任務隊列來完成操作。
    我們知道,當程序開始運行,會執行main方法,Android程序一般來說執行在main方法所在的線程中——“主線程”,主線程也不是什么特別的線程,也是使用new Thread()方法來創建的,看下面的代碼:
public class HelloRunnable implements Runnable {
    public void run() {
        System.out.println("Hello from a thread!");
    }
    public static void main(String args[]) {
        (new Thread(new HelloRunnable())).start();
    }
}

不過,這跟我們所知道的Android主線程不同的是,run()方法執行完,程序就退出了。在Android中,這顯然不是我們需要的,我們希望主程序初始化完成后,等待用戶的操作,反饋用戶的操作并執行接下來的任務。并且用戶可能進行一系列連續的操作,我們需要一個先進先出的隊列來緩沖用戶操作并交給程序執行,這就是Looper的作用,在任何GUI系統里,都有一樣的機制來完成這個目的。
而且Looper的官網說:

"Threads by default do not have a message loop associated with them", and Looper is a class "used to run a message loop for a thread".

這樣就更好理解Looper了。

為什么證明我們的描述,我們去ActivityThread class看它的main方法,把一個普通Thread變成了主線程:

        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");

好了,大概就是這樣了先,有問題一起探討~ 終于把這個寫了,居然寫了一天...

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容