Android Looper 和 Handler 教學

Photo by Willian Justen de Vasconcellos on Unsplash
Photo by Willian Justen de Vasconcellos on Unsplash
Looper 和 Handler 是 Android 的核心之一,很多高階元件都是建構在它們之上。了解它們有助於我們了解一些核心元件的運作。本文章將介紹 Looper 和 Handler 以及相關的元件。

Looper 和 Handler 是 Android 的核心之一,很多高階元件都是建構在它們之上。了解它們有助於我們了解一些核心元件的運作。本文章將介紹 Looper 和 Handler 以及相關的元件。

概覽

Android 的 LooperHandler 與一般的 thread 很像,主要差別於,它們維護了一個 MessageQueue,並且一個一個地處理 Message。因此,當我們需要執行多個非同步任務,並想避免 race conditions 時,Looper 和 Handler 可能會是個很好的選擇。Android 的 main thread 就是基於它們實作的。另外,AsyncTask 也是建構在 Looper 和 Handler 之上。

Looper 和 Handler 的使用方式非常簡單。先讓我們來看看一個簡單的範例。

宣告一個 MyHandler、繼承 Handler、並 override handleMessage(Message) 方法。當一個 message 要被處理時,handleMessage(Message) 就會被呼叫,我們必須在裡面實作如何處理這個 message。

class MyHandler(looper: Looper) : Handler(looper) {
    override fun handleMessage(msg: Message) {
        // Implementation to handle the message.
    }
}

接下來,建立一個 HandlerThread,並呼叫 HandlerThread.start() 來啟動它。HandlerThread 裡面包含一個 Looper,而 Looper 裡面又包含了一個 MessageQueue。此時 HandlerThread.run() 裡會呼叫 Looper.loop()。Looper.loop() 就開始從 MessageQueue 裡面一個一個地取出 Message 並處理它。不過,目前為止,MessageQueue 還是空的,所以 HandlerThread 會被 blocked,直到 MessageQueue 回傳一個 Message。

建立我們剛剛實作的 MyHandler,並將它與 HandlerThread 裡的 Looper 綁定在一起。當我們要建立一個 Message 時,我們不會直接建立 Message,而是透過呼叫 Handler.obtainMessage() 來取得一個 Message。它會自動將 Message.target 設定為自已。所以,當 Looper.loop() 在處理 Message 時,就會知道要呼叫哪個 Handler 來處理這個 Message。然後呼叫 Handler.sendMessage() 來將 Message 送給剛剛綁定的 Looper 裡的 MessageQueue。

最後,我們要呼叫 HandlerThread.quitSafely() 來結束 HandlerThread。它會清空 MessageQueue, 然後 MessageQueue 回傳 null 給 Looper.loop()。當 Looper.loop() 收到 null 時,它就會停止再去取 Message,所以 HandlerThread 就會停止。

// Initialize
val handlerThread = HandlerThread("HandlerThread", Process.THREAD_PRIORITY_BACKGROUND)
handlerThread.start()
val myHandler = MyHandler(handlerThread.looper)

// Send a message
val message = myHandler.obtainMessage().apply { arg1 = 1 }
myHandler.sendMessage(message)

// Quit the HandlerThread when you don't need it
handlerThread.quitSafely()

如果你只是想了解如何使用 Looper 和 Handler 的話,看到這裡就可以了。以下會介紹內部的運作機制。

架構

下圖顯示所有相關的元件之間的關係。

Looper and Handler Architecture
Looper and Handler Architecture

下圖顯示這些元件之間的運作流程。當 HandlerThread 啟動後,HandlerThread.run() 會呼叫 Looper.parepare() 來初始化 Looper,然後呼叫 Looper.loop()。

Looper.loop() 裡面是一個 for loop,會一直呼叫 loopOnce()。如果 loopOnce() 回傳 false,那 loop() 就結束 for loop。loopOnce() 會呼叫 MessageQueue.next() 來取得下一個待處理的 Message。Message.target 的型態是 Handler。當取得一個 Message 後,loopOnce() 就會呼叫 Message.target.dispatchMessage(),而它會呼叫 Message.target.handleMessage() 來處理 Message。

當 MessageQueue 是空的時候,next() 會被 blocked,以至於 HandlerThread 會被 blocked。而,當呼叫 HandlerThread.quitSafely() 時,它會清空 MessageQueue,並設定 MessageQueue.mQuitting 為 true。而且,如果此時 next() 被 blocked 的話,它會被喚醒。當 next() 檢查 mQuitting 為 true 時,就會回傳 null。loopOnce() 從 next() 取得 null 時,就會回傳 false 給 loop()。最後,loop() 就會結束 for loop。

Flow of Looper and Handler
Flow of Looper and Handler

Message

以下是 Message 的程式碼片段。我們移除了很多程式碼,如果你想要看完整的程式碼,請參照 Message.java

public final class Message implements Parcelable {
    public int what;
    public int arg1;
    public int arg2;
    public Object obj;
    public Messenger replyTo;

    static final int FLAG_IN_USE = 1 << 0;
    int flags;
    public long when;
    Bundle data;
    Handler target;
    Runnable callback;
    Message next;

    private static Message sPool;
    private static int sPoolSize = 0;
    private static final int MAX_POOL_SIZE = 50;

    public static Message obtain() {
        if (sPool != null) {
            Message m = sPool;
            sPool = m.next;
            m.next = null;
            m.flags = 0;
            sPoolSize--;
            return m;
        }
        return new Message();
    }

    public static Message obtain(Handler h) {
        Message m = obtain();
        m.target = h;
        return m;
    }

    public void recycle() {
        if (isInUse()) {
            return;
        }
        recycleUnchecked();
    }

    void recycleUnchecked() {
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        when = 0;
        target = null;
        callback = null;
        data = null;
        if (sPoolSize < MAX_POOL_SIZE) {
            next = sPool;
            sPool = this;
            sPoolSize++;
        }
    }

    public Message() {
    }
}

MessageQueue

以下是 MessageQueue 的程式碼片段。我們移除了很多程式碼,如果你想要看完整的程式碼,請參照 MessageQueue.java

next() 會被 blocked 在呼叫 nativePollOnce() 直到被 nativeWake() 喚醒。然後,會嘗試取出並回傳下一個待處理的 Message。如果,queue 是空的的話,則呼叫 nativePollOnce() 繼續等待,或是發現 mQuitting 是 true 時,則回傳 null。nativePollOnce() 和 nativeWake() 是 JNI methods,有興趣的讀者可參照 Looper.cpp

在 quit() 中,當 safe 為 true 時,會呼叫 removeAllFutureMessagesLocked(),而為 false 時,則呼叫 removeAllMessagesLocked()。差別在於,removeAllMessagesLocked() 會移除所有的未處理的 Message。而 removeAllFutureMessagesLocked() 則只有移除 Message.when 大於目前時間的 Message,所以那些 Message.when 小於目前時間的 Message,依舊會被處理。

public final class MessageQueue {
    private final boolean mQuitAllowed;
    Message mMessages;
    private boolean mQuitting;

    private native void nativePollOnce(long ptr, int timeoutMillis);
    private native static void nativeWake(long ptr);

    MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
    }

    Message next() {
        for (; ; ) {
            nativePollOnce(ptr, nextPollTimeoutMillis);
            
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            if (msg != null && msg.target == null) {
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null);
            }
            if (msg != null) {
                if (now >= msg.when) {
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    msg.markInUse();
                    return msg;
                }
            }

            if (mQuitting) {
                return null;
            }
        }
    }

    void quit(boolean safe) {
        if (!mQuitAllowed) throw new IllegalStateException("Main thread not allowed to quit.");
        if (mQuitting) return;

        mQuitting = true;
        if (safe) {
            removeAllFutureMessagesLocked();
        } else {
            removeAllMessagesLocked();
        }
    }

    private void removeAllMessagesLocked() {
        Message p = mMessages;
        while (p != null) {
            Message n = p.next;
            p.recycleUnchecked();
            p = n;
        }
        mMessages = null;
    }

    private void removeAllFutureMessagesLocked() {
        final long now = SystemClock.uptimeMillis();
        Message p = mMessages;
        if (p != null) {
            if (p.when > now) {
                removeAllMessagesLocked();
            } else {
                Message n;
                for (; ; ) {
                    n = p.next;
                    if (n == null) {
                        return;
                    }
                    if (n.when > now) {
                        break;
                    }
                    p = n;
                }
                p.next = null;
                do {
                    p = n;
                    n = p.next;
                    p.recycleUnchecked();
                } while (n != null);
            }
        }
    }

    boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) throw new IllegalArgumentException("Message must have a target.");

        if (msg.isInUse()) throw new IllegalStateException(msg + " This message is already in use.");

        if (mQuitting) {
            msg.recycle();
            return false;
        }

        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        if (p == null || when == 0 || when < p.when) {
            msg.next = p;
            mMessages = msg;
        } else {
            Message prev;
            for (; ; ) {
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    break;
                }
            }
            msg.next = p;
            prev.next = msg;
        }

        return true;
    }
}

Handler

以下是 Handler 的程式碼片段。我們移除了很多程式碼,如果你想要看完整的程式碼,請參照 Handler.java

所有的 post 和 send methods 最後都呼叫 enqueueMessage()。Handler 會將自己設定給 Message.target,然後呼叫 MessageQueue.enqueueMessage() 來將 Message 放入 queue 裡面等待處理。

當 Message 要被處理時,Looper.loopOnce() 呼叫 Message.target.dispatchMessage() 也就是 Handler.dispatchMessage(),其會呼叫 Handler.handleMessage()。這個 method 是空的,因為這有待開發者實作處理 Message 的程式碼。

public class Handler {
    private static Handler MAIN_THREAD_HANDLER = null;

    final Looper mLooper;
    final MessageQueue mQueue;
    final Callback mCallback;
    final boolean mAsynchronous;

    public interface Callback {
        boolean handleMessage(Message msg);
    }

    public void handleMessage(Message msg) {
    }

    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

    private static void handleCallback(Message message) {
        message.callback.run();
    }

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

    public static Handler getMain() {
        if (MAIN_THREAD_HANDLER == null) {
            MAIN_THREAD_HANDLER = new Handler(Looper.getMainLooper());
        }
        return MAIN_THREAD_HANDLER;
    }

    public final Message obtainMessage() {
        return Message.obtain(this);
    }

    public final boolean post(Runnable r) {
        return sendMessageDelayed(getPostMessage(r), 0);
    }

    public final boolean postAtTime(Runnable r, long uptimeMillis) {
        return sendMessageAtTime(getPostMessage(r), uptimeMillis);
    }

    public final boolean postDelayed(Runnable r, long delayMillis) {
        return sendMessageDelayed(getPostMessage(r), delayMillis);
    }

    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

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

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

    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        return enqueueMessage(queue, msg, uptimeMillis);
    }

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();
        return queue.enqueueMessage(msg, uptimeMillis);
    }
}

Looper

以下是 Looper 的程式碼片段。我們移除了很多程式碼,如果你想要看完整的程式碼,請參照 Looper.java

loop() 不斷地呼叫 loopOnce(),直到它回傳 false 才結束。而,loopOnce() 從 MessageQueue.next() 取得一個 Message,並呼叫 Message.target.dispatchMessage() 來處理Message。

public final class Looper {
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    private static Looper sMainLooper;  // guarded by Looper.class
    final MessageQueue mQueue;
    final Thread mThread;
    private boolean mInLoop;

    public static void prepare() {
        prepare(true);
    }

    private static void prepare(boolean quitAllowed) {
        sThreadLocal.set(new Looper(quitAllowed));
    }

    public static void prepareMainLooper() {
        prepare(false);
        sMainLooper = myLooper();
    }

    public static Looper getMainLooper() {
        return sMainLooper;
    }

    private static boolean loopOnce(Looper me, long ident, int threshold) {
        Message msg = me.mQueue.next();
        if (msg == null) return false;

        msg.target.dispatchMessage(msg);

        msg.recycleUnchecked();

        return true;
    }

    public static void loop() {
        Looper me = myLooper();
        me.mInLoop = true;

        for (; ; ) {
            if (!loopOnce(me, ident, thresholdOverride)) {
                return;
            }
        }
    }

    public static Looper myLooper() {
        return sThreadLocal.get();
    }

    public static MessageQueue myQueue() {
        return myLooper().mQueue;
    }

    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

    public boolean isCurrentThread() {
        return Thread.currentThread() == mThread;
    }

    public void quit() {
        mQueue.quit(false);
    }

    public void quitSafely() {
        mQueue.quit(true);
    }

    public MessageQueue getQueue() {
        return mQueue;
    }
}

HandlerThread

以下是 HandlerThread 的程式碼片段。我們移除了很多程式碼,如果你想要看完整的程式碼,請參照 HandlerThread.java

HandlerThread.run() 先呼叫 Looper.prepare() 再呼叫 Looper.loop(),其邏輯相當地簡單。雖然你也可以自己宣告一個 class 並繼承 Thread,不過還是建議使用 HandlerThread。

public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;
    private Handler mHandler;

    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }

    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }

    protected void onLooperPrepared() {
    }

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }

    public Looper getLooper() {
        return mLooper;
    }

    public Handler getThreadHandler() {
        if (mHandler == null) {
            mHandler = new Handler(getLooper());
        }
        return mHandler;
    }

    public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }

    public boolean quitSafely() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quitSafely();
            return true;
        }
        return false;
    }
}

結語

看完本文章後,是不是發現 Looper 和 Handler 的整個運作機制並不是太複雜。只是元件間的引用很容易會讓人混淆。當我們需要非同步執行一些任務,又不希望有 race conditions 的情況發生時,Looper 和 Handler 會是很好用的工具。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

You May Also Like
Photo by Hans-Jurgen Mager on Unsplash
Read More

Kotlin Coroutine 教學

Kotlin 的 coroutine 是用來取代 thread。它不會阻塞 thread,而且還可以被取消。Coroutine core 會幫你管理 thread 的數量,讓你不需要自行管理,這也可以避免不小心建立過多的 thread。
Read More