天天看點

Android Handler機制

        Android開發中經常用到handler來發送消息,比如将消息抛到主線程去更新UI。handler機制涉及到四個核心的類:

  • Looper:消息循環,有一個Messagequenue,不斷從消息隊列中取出消息;
  • MessageQuenue:消息隊列,裡邊包含Message;
  • Message:消息,裡邊有一個Handler,負責處理該消息;
  • Handler:管理消息隊列,裡邊有Looper和MessageQueue;
Android Handler機制

        為了更好的了解這三者的關系,我們可以想象成現實生活中,工廠裡邊的一條流水線。有一個放産品(Message)的傳送帶(MessageQuenue),被機器帶動起來不斷循環運作(Looper),而勞工(Handler)需要将産品放到傳送帶上,然後傳送到指定位置時再将産品進行下一步處理。

Android Handler機制

        從上述表達的關系可以看出,有一個MessageQuenue,可以往裡邊仍Message,而Looper則要讓MessageQuenue轉起來,不斷從裡邊取消息;而Handler負責把Message放到隊列裡邊,最後再處理這個消息。

注:以下源碼基于Android 9.0(版本名稱:  Pie     API Level:  28)。

Looper類分析

frameworks/base/core/java/android/os/Looper.java    

在該檔案中給我們提供了一個執行個體:

* <pre>
  *  class LooperThread extends Thread {
  *      public Handler mHandler;
  *
  *      public void run() {
  *          // ①調用prepare
  *          Looper.prepare();
  *
  *          mHandler = new Handler() {
  *              public void handleMessage(Message msg) {
  *                  // process incoming messages here
  *              }
  *          };
  *          // ② 進入消息循環
  *          Looper.loop();
  *      }
  *  }</pre>
           

上述使用了Looper的兩個關鍵調用(①和②),下面逐一分析。

Looper準備:

public static void prepare() {
        prepare(true);
    }
    private static void prepare(boolean quitAllowed) {
        // 一個Looper隻能調用一次prepare
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        // 構造一個Looper對象,設定到調用線程的局部變量中
        sThreadLocal.set(new Looper(quitAllowed));
    }
    // sThreadLocal定義
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
           

ThreadLocal是一個泛型,是Java線程中的局部變量。全名是Thread Local Variable。具體可以自行研究源碼:libcore/ojluni/src/main/java/java/lang/ThreadLocal.java

Looper構造函數:

private Looper(boolean quitAllowed) {
        // 構造一個消息隊列
        mQueue = new MessageQueue(quitAllowed);
        // 擷取目前線程的Thread對象
        mThread = Thread.currentThread();
    }
           

prepare主要建立了Looper對象,并儲存在調用線程的ThreadLocal中。同時Looper對象内部封裝了一個消息隊列。prepare通過ThreadLocal機制将Looper和調用線程關聯。Looper循環:

/**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    public static void loop() {
        // 傳回儲存在調用線程的TLV中的Looper對象。
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        // 取出Looper的消息隊列
        final MessageQueue queue = me.mQueue;

        // ... 細節省略

        for (; ; ) {
            // 從消息隊列中取出消息
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            // ... 細節省略
            try {
                // 調用該消息的Handler,交給它的dispatchMessage函數處理
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            // ... 細節省略
            msg.recycleUnchecked();
        }
    }

    /**
     * Return the Looper object associated with the current thread.  Returns
     * null if the calling thread is not associated with a Looper.
     *      
     */
    public static @Nullable
    Looper myLooper() {
        return sThreadLocal.get();
    }
           

通過上面源碼分析可以了解到Looper的作用:

  • 封裝一個消息隊列(Messagequenue);
  • Looper的prepare函數将Looper和調用prepare的線程(即處理線程)綁定;
  • loop函數啟動一個無線循環來處理消息隊列中的消息。

        當事件源向這個Looper發送消息的時候,其實是把消息加到這個Looper的消息隊列裡了。那麼,該消息就将由和Looper綁定的處理線程來處理。可事件源又是怎麼向Looper消息隊列添加消息的呢?下面需要了解Looper、Message、Handler的關系。

  • Looper中有一個Message隊列,裡邊存儲的是一個個待處理的Message;
  • Message中有一個Handler,這個Handler是用來處理Message的。

其中Handler類封裝了很多瑣碎的工作。具體:

frameworks/base/core/java/android/os/Handler.java

final Looper mLooper;    // 有一個Looper
    final MessageQueue mQueue;    // 有一個消息隊列
    final Callback mCallback;    // 有一個回調用的類
    final boolean mAsynchronous;  // 異步相關設定,本文不做分析
           

Handler有幾個構造函數,主要差別是對上面三個重要成員變量初始化上。

// 構造函數1
    public Handler() {
        this(null, false);
    }

    // 構造函數2
    public Handler(Callback callback) {
        this(callback, false);
    }
    // 構造函數3
    public Handler(Looper looper) {
        this(looper, null, false);
    }
    // 構造函數4
    public Handler(Looper looper, Callback callback) {
        this(looper, callback, false);
    }
    // 構造函數5
    public Handler(boolean async) {
        this(null, async);
    }

    public Handler(Callback callback, boolean async) {
        // ... 細節省略
        // 獲得調用線程的Looper
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        // 得到Looper的消息隊列
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

    public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
           

        上述構造函數中,Handler中的消息隊列變量最終都會指向Looper的消息隊列,Handler提供了一系列函數,幫助我們完成建立消息和插入消息隊列的工作。這裡列出一部分分析:

// 檢視消息隊列中是否有消息碼是what的消息
public final boolean hasMessages(int what)
// 從Handler中建立一個消息碼是what的消息
public final Message obtainMessage(int what)
// 從消息隊列中移除消息碼是what的消息
public final void removeMessages(int what)
// 發送一個隻填充了消息碼的消息
public final boolean sendEmptyMessage(int what)
// 發送一個消息,該消息添加到隊列尾
public final boolean sendMessage(Message msg)
// 發送一個消息,該消息添加到隊列頭,是以優先級很高
public final boolean sendMessageAtFrontOfQueue(Message msg)
           

        以sendMessage為了,其代碼實作如下所示:

public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }

    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        // 把Message的target設為自己,然後加入到消息隊列
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
           

Handler的消息處理

        剛才,我們往Looper的消息隊列中加入了一個消息,按照Looper的處理規則,它在擷取消息後會調用target的dispatchMessage函數,再把這個消息派發給Handler處理。

public void dispatchMessage(Message msg) {
        // 如果Message本身有callback,則直接交給Message的callback處理
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            // 如果本Handler設定mCallback,則交給mCallback處理
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            // 最後才交給子類處理
            handleMessage(msg);
        }
    }
           

        通常情況下,我們一般都是采用第三種方法,即在子類中通過重載handleMessage來完成處理工作。

        要了解Handler機制,主要是弄清楚Looper、MessageQuenue、Message、Handler之間的關系。為了便于了解,我們可以把這套架構想象成現實生活中工廠流水線,勞工(Handler)拿到産品(Message),放在傳送帶(MessageQuenue)上,同時做了一個标記(msg.target = this;),傳送帶由機器帶動(Looper),然後當機器将産品傳遞到另一位置時,該員工根據産品上的标記,負責處理該産品,分發到不同的地方。

        上述分析細節可以自行檢視Android源碼,推薦網站:http://www.androidos.net.cn/sourcecode

擴充:

        Looper類裡邊啟動了一個for(;;)循環,如果消息隊列時會不會一直占用CPU時間片?

        循環中queue.next()函數裡邊用到了pipe/epoll機制,確定不會占用CPU時間片。之後會通過源碼進行分析。