天天看點

Android的消息循環機制 Looper Handler類分析

Android中的Looper類和Handler類解析,解釋了Android中的消息循環機制,附有源碼分析,在處理Android線程的時候可以更好地了解問題。

Android的消息循環機制 Looper Handler類分析

Looper類說明

   

Looper

 類用來為一個線程跑一個消息循環。

  線程在預設情況下是沒有消息循環與之關聯的,Thread類在run()方法中的内容執行完之後就退出了,即線程做完自己的工作之後就結束了,沒有循環的概念。

  調用Looper類的 

prepare()

 方法可以為目前線程建立一個消息循環,調用

loop()

 方法使之處理資訊,直到循環結束。

  大多數和消息循環的互動是通過 

Handler

 類進行的。

  下面是一個典型的實作:

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();
      }
  }      

Handler類說明  

  Handler類用來發送和處理消息(Message)以及和線程的消息隊列(

MessageQueue

)關聯的Runnable對象。

  每一個Handler對象都僅和一個線程及這個線程的消息隊列關聯。

  一個特定線程的所有Handler對象都會收到同樣的方法。(這是一個“一對多”的關系)。

  當你建立一個新的Handler對象,它會和建立它的這個線程/線程的消息隊列綁定,從那個時刻開始,它将向這個消息隊列傳遞消息和runnable對象,并且當它們從隊列中出來時執行它們。

  Handler主要有兩種用途:

  1.合理排程安排消息和runnable對象,使它們在将來的某個點被執行。

  2.将一個動作入隊安排在非目前線程執行。

  排程消息是通過一系列的post方法和sendMessage方法。

  post方法允許你向消息隊列中入隊一些Runnable對象,在它們被接收到的時候會被調用,(實際上post方法也就是将runnable對象包裝在消息裡,然後再通過sendMessage方法實作),post方法有:

  post(Runnable r)

  postAtFrontOfQueue(Runnable r)

  postAtTime(Runnable r, Object token, long uptimeMillis)

  postAtTime(Runnable r, long uptimeMillis)

  postDelayed(Runnable r, long delayMillis)

  sendMessage方法允許你入隊一個消息對象(Message),包含一個bundle資料,之後将會被Handler的handleMessage(Message)方法所處理。

  (這個需要你實作一個Handler的子類)。

  sendMessage方法有:

  sendEmptyMessage(int what)

  sendEmptyMessageAtTime(int what, long uptimeMillis)

  sendEmptyMessageDelayed(int what, long delayMillis)

  sendMessage(Message msg)

  sendMessageAtFrontOfQueue(Message msg)

  sendMessageAtTime(Message msg, long uptimeMillis)

  sendMessageDelayed(Message msg, long delayMillis)

  一個線程對應一個Looper,有一個消息隊列,但是可以關聯多個Handlers。

UI線程和非UI線程的通信

  當你的應用程序被建立的時候,應用程序的主線程(main thread)就建立一個消息隊列,操縱top級别的應用對象(比如activities、broadcast receivers等)和它們建立的任何視窗。

  因為效率的考慮,所有的View和Widget都不是線程安全的,是以相關操作強制放在同一個線程,這樣就可以避免多線程帶來的問題。這個線程就是主線程,也即UI線程。

  你可以建立自己的線程,通過一個Handler對象和應用的主線程通信。

  如果你将一個Handler和你的UI線程連接配接,處理消息的代碼就将會在UI線程中執行。

  新線程和UI線程的通信是通過從你的新線程調用和主線程相關的Handler對象的post或者sendMessage方法實作的,給定的Runnable或Message将會在Handler的消息隊列中,并且在合适的時間被處理。

  總的來說,共有5種方式從非UI線程和UI線程通信:

  • Activity.runOnUiThread(Runnable)
  • View.post(Runnable)
  • View.postDelayed(Runnable, long)

  還有就是通過Handler,或者使用AsyncTask。

  具體參見之前的博文:http://www.cnblogs.com/mengdd/p/3418780.html

消息循環

  消息處理機制中,消息存放在一個消息隊列中,而線程圍繞這個隊列進入一個無限循環,直到程式退出。

  如果隊列中有消息,線程就會把消息取出來,并分發給相應的Handler進行處理;

  如果隊列中沒有消息,線程就會進入空閑等待狀态,等待下一個消息的到來。

Android的主線程循環建立

  Android程式的運作入口點可以認為是android.app.ActivityThread類的main()方法(源碼2.3.3):

public static final void main(String[] args) {
        // other codes...

        // 建立主線程循環
        Looper.prepareMainLooper();
        if (sMainThreadHandler == null) {
            sMainThreadHandler = new Handler();
        }

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

        // other codes...

        // 進入目前線程(此時是主線程)消息循環
        Looper.loop();

        // other codes...

        thread.detach();
        // other codes...
    }      

  這個main()方法裡面為程式建立了主線程循環。

  Looper類中的主線程建立方法prepareMainLooper():

/**
     * Initialize the current thread as a looper, marking it as an application's
     * main looper. The main looper for your application is created by the
     * Android environment, so you should never need to call this function
     * yourself. {@link #prepare()}
     */

    public static final void prepareMainLooper() {
        prepare();
        setMainLooper(myLooper());
        // other codes...
    }      

  上面這個方法是專門為建立應用程式的主線程調用的,其他線程都不應該調用這個方法,而應該調用prepare()方法。

  主線程的Looper對象建立好之後會存在Looper類的成員變量mMainLooper裡,通過一個get方法可以擷取到:

/**
     * Returns the application's main looper, which lives in the main thread of
     * the application.
     */
    public synchronized static final Looper getMainLooper() {
        return mMainLooper;
    }      

  這樣之後,程式中其他線程就可以擷取主線程的消息循環對象,進而和主線程通信。

線程建立消息循環:Looper.prepare()

  非主線程建立消息循環時,調用的是Looper類的prepare()方法,其實建立主線程的方法實質也調用了prepare方法:

/**
     * Initialize the current thread as a looper. This gives you a chance to
     * create handlers that then reference this looper, before actually starting
     * the loop. Be sure to call {@link #loop()} after calling this method, and
     * end it by calling {@link #quit()}.
     */
    public static final void prepare() {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException(
                    "Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper());
    }      

  這個方法會調用Looper類的私有構造方法,建立Looper類對象。

private Looper() {
        // 私有構造方法,在prepare()方法裡面調用
        // 建立消息隊列
        mQueue = new MessageQueue();
        mRun = true;
        // 目前線程
        mThread = Thread.currentThread();
    }      

進入消息循環:Looper.loop()

  不管是不是主線程,prepare之後需要調用Looper類的loop()方法,可以看作是進入消息循環:

/**
     * Run the message queue in this thread. Be sure to call {@link #quit()} to
     * end the loop.
     */
    public static final void loop() {
        // 進入目前線程的消息循環
        Looper me = myLooper();
        MessageQueue queue = me.mQueue;
        while (true) {

            // 從隊列中取出消息
            Message msg = queue.next(); // might block

            if (msg != null) {
                if (msg.target == null) {
                    // No target is a magic identifier for the quit message.
                    return;
                }
                // other codes...

                // 分發消息
                msg.target.dispatchMessage(msg);
                // 消息的target是Handler類型的對象

                // other codes...

                // 釋放清理
                msg.recycle();
            }
        }
    }      

消息分發和處理——Handler

  前面建立了消息循環,并且進入了這個循環,但是消息隊列中的消息是如何加入和處理的呢?是通過Handler。

Handler構造:

  Handler有幾個構造重載,如果構造時不提供Looper類對象參數,會擷取目前線程的Looper對象,即将目前線程的消息循環作為Handler關聯的消息循環。

  前面說過,不是所有線程都有一個消息循環,是以如果目前線程沒有消息循環,而構造Handler對象時又沒有指定Looper對象,則會抛出一個運作時異常:

mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }      

  如果沒有抛出異常,Handler對象構造好之後,它就關聯了相應的Looper執行個體和消息隊列執行個體,即完成綁定。

消息發送:

  Handler對象的post方法和sendMessage方法本質上都是發送消息的方法(post類方法實質上是調用了sendMessage方法)。

  所謂發送消息就是把消息放入消息隊列中的合适位置,并且把消息的target設定為本Handler對象。

  (這裡将消息加入隊列,也有一些什麼線程喚醒的事兒咱們不深入讨論了)。

  可以添加,也就相應地有一些移除方法。

消息處理:

  在上面的Looper.loop()方法中,調用了消息對象target(即發送這個消息的Handler對象)的dispatchMessage()方法。

  /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {

        // 首先,處理Message自己的callback,調用其run方法
        if (msg.callback != null) {
            handleCallback(msg);
        }
        else {
            // 其次,調用Handler自留的接口對象
            // 這個成員變量聲明時的注釋如下:
            /**
             * Callback interface you can use when instantiating a Handler to
             * avoid having to implement your own subclass of Handler.
             */
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }

            // 最後,調用handleMessage方法處理消息,Handler類中這個方法為空,子類可以重寫這個方法
            handleMessage(msg);
        }
    }      

  Handler類的handleMessage()方法預設實作為空:

/**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(Message msg) {
    }      

  上面的代碼中也解釋了為什麼一個消息隊列可以關聯很多個Handler對象,因為雖然隊列隻有一個,但是消息的target是當時把它加入的Handler對象。

  是以當隊列中的消息處理的時候,也會找到當時送它來的Handler對象,調用其相應的dispatchMessage()方法,進而調用其中的handleMessage()方法或者mCallback成員的handleMessage()方法來進行處理。

參考資料

  Handler:http://developer.android.com/reference/android/os/Handler.html

  Looper:http://developer.android.com/reference/android/os/Looper.html

  比較好的幾個博文:

  Android應用程式線程消息循環模型分析:http://blog.csdn.net/luoshengyang/article/details/6905587

   Android應用程式消息處理機制(Looper、Handler)分析:http://blog.csdn.net/luoshengyang/article/details/6817933

  Android的消息隊列模型:http://www.cnblogs.com/ghj1976/archive/2011/05/06/2038469.html

  Android中的Handler, Looper, MessageQueue和Thread:http://www.cnblogs.com/xirihanlin/archive/2011/04/11/2012746.html

  本部落格其他相關博文:

  Android中的UI線程與非UI線程:http://www.cnblogs.com/mengdd/p/3418780.html

  說明:本文相關源碼是Android 2.3.3版本的。

作者: 聖騎士Wind

出處: 部落格園: 聖騎士Wind

Github: https://github.com/mengdd

微信公衆号: 聖騎士Wind

Android的消息循環機制 Looper Handler類分析