Android Handler 運行機制

什麼是 Handler?有什麼用?

在 android 應用開發中經常用到 Handler ,大概知道 Handler 常被用來處理線程間通信,比如:在子線程中處理某個邏輯後通過 Handler 發送消息的方式,切換回 UI 線程更新界面

Handler 到底是什麼呢?總能聽到最傻瓜式的問法,那麼就來簡單回答一下:Handler 不是一個具體的對象,暫且說是一套解決方案吧,在 Android 中處理消息 (Message)的一套方案。
我們通過研究 Handler 的運行機制來了解 android 的消息機制

什麼是 Handler 運行機制?

這裏需要引入幾個概念和名詞 Message / MessageQueue / Looper
以下請允許我用不太恰當地方式簡單解釋一下這幾個概念

Message : 消息
MessageQueue : 消息隊列
Looper : 無限循環地從隊列中獲取消息交給 Handler
Handler : 發送消息,處理消息

如果有人問你, Handler 運行機制 是怎麼樣的,你就告訴他:

Handler 發送消息(Message) 到消息隊列中(MessageQueue) ,Looper 通過無限循環的方式不斷向消息隊列中獲取新添加的消息 然後交給 Handler ,最終消息回到 Handler 中被處理

(平常開發中)由於 Handler 創建在主線程,處理消息的方法也運行在主線程,因此上述中消息被 Looper 交回給 Handler 的時候就實現了“線程切換”

喔~這個6,這個6,這個思想非常棒,仔細想想在子線程中做了一堆操作,什麼添加消息到隊列,我們不管,只認準最後 Looper 把消息交回給了 Handler ,又因爲 Handler 運行在主線程中,因此最後處理消息時就在主線程了(根本原因是創建Handler 的 Looper 默認使用主線程的 Looper )

理論往往是抽象枯燥的,我們總希望通過熟悉的代碼來慢慢體會,來看下日常開發中常用Handler使用場景,在子線程中處理耗時邏輯,然後再發送消息到主線程中更新UI

在主線程中創建Handler處理類

    class MyHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 1:
                    Log.e("tag", "thread name:" + Thread.currentThread().getName());
                    //更新UI
                    break;
            }
        }
    }

    MyHandler myHandler = new MyHandler();

創建線程,處理耗時邏輯,然後發送消息

        new Thread("t1") {
            @Override
            public void run() {
                //耗時操作
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Message message = Message.obtain();
                message.what = 1;
                //發送消息
                myHandler.sendMessage(message);
            }
        }.start();

這裏可以看到,我們在主線程(activity運行在主線程中)創建Handler,然後創建一個子線程 t1 ,在 t1 中處理耗時邏輯,再通過 sendMessage() 發送消息,就是這麼一個簡單的代碼,能夠實現”線程切換”的效果,到底是怎麼實現的呢?

開發中我們能接觸到的只有 Handler ,對於 MessageQueue 和 Looper 這兩個概念比較陌生,就上述的例子中也完全沒有體現這些字眼,但是對於 Handler 的運行他們起到了不可缺少的重要作用。

Handler 通過 sendMessage 發送消息後,將 Message 發送到了哪裏呢? 又是怎麼拿到這些 Message 的呢?

MessageQueue 消息隊列工作原理

MessageQueue 翻譯爲消息隊列。簡單的說就是裝載消息(Message) 的容器。容器?那不是很簡單,只要負責 添加讀取(移除) 內容就好了。

當 Handler 通過 sendMessage 方法發送 Message 時, MessageQueue 就會調用 enqueueMessage 方法把這條消息添加到自身容器中。然後呢?然後就放着唄,等着別人來取,當有人來取 Message 的時候,通過 next 方法取出一條消息並將消息從自身容器中移除,這就是消息隊列的工作職責

這裏注意,雖然叫做消息隊列,但是內部實現並不是用隊列,而是單鏈表的數據結構來維護消息列表

MessageQueue : 添加消息到隊列 enqueueMessage

上面說 sendMessage 時 MessageQueue 就會調用自身的 enqueueMessage 將消息添加到自身容器,怎麼實現的呢?Handler 和 MessageQueue 兩者又是怎麼關聯起來的?這時候我們就需要通過閱讀一下源碼啦

等一下,別走啊,這源碼很簡單的,不用一頭扎入,看個大概思路就行

我們知道 Handler 發送消息的方法有很多,先不管,需要知道的是最後調用的都是同一個 sendMessageAtTime()

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

這個就是 Handler 的 sendMessageAtTime 方法的源碼啦,很簡單,將一個 mQueue (特別注意這個 mQueue)賦值給 MessageQueueMessageQueue 如果爲空,拋異常,不爲空當做一個參數傳遞給 enqueueMessage (Handler 的方法),恩,這裏問題不大,很普通的寫法,接着看看 Handler 的 enqueueMessage 方法

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

丟掉前面無關的代碼,直接聚焦 queue.enqueueMessage(msg, uptimeMillis) 這行代表表示,最終調用了 MessageQueueenqueueMessage 方法將消息添加進隊列

到這裏已經很清晰可以看到 MessageQueue 將消息加入隊列的邏輯了,不用看都知道, enqueueMessage 具體實現肯定是操作鏈表添加內容

怎麼樣?是不是很簡單,很清晰的看到

Handler 調用 sendMessage 方法時 MessageQueue 會通過 enqueueMessage 方法將 message 添加到自身容器中

嗯,細心的同學或許就會問了,這個 MessageQueue 是哪裏來的,你突然引入一個 MessageQueue 然後就開始添加 消息了,也沒說他是哪裏崩出來的呀

回到剛纔那個 sendMessageAtTime() 方法,第一行代碼出現:MessageQueue queue = mQueue;
原來 MessageQueue 是通過 mQueue 賦值的。

在 Handler 類中搜索 mQueue ,找啊找啊,最後在 Handler 構造方法中找到它

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

沒錯就是它 mQueue ,它是通過 looper.mQueue 得到的

而且我們通過其他的構造函數發現 Handler 的所有構造函數最終都是調用這個方法,也就是說,我們在創建 Handler 的時候就創建了這個 MessageQueue

soga 原來 MessageQueue 是通過 Looper 得到的,那 Looper 又是什麼?乘勝追擊看看這個 Looper 是什麼。

Looper 工作原理

Looper 在 android 消息機制中扮演着 循環從隊列中獲取消息的角色,就是說它會無限循環的查詢 消息隊列中是否有新的消息,如果有就獲取到這個消息,然後交給Handler,如果沒有就阻塞在那裏

先看看 Looper 的構造方法

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

在 Looper 構造方法中,創建了一個 MessageQueue 即消息隊列,然後獲取當前線程保存起來。

我們知道 Handler 工作需要 Looper 的支持,如果沒有 Looper 的線程中使用 Handler 就會報錯,那如何爲一個線程創建一個 Looper 呢? 很簡單, 通過 Looper.prepure() 就可以爲一個線程創建 Looper ,接着調用 Looper.loop() 來開啓消息循環。示例如下

       new Thread("t1") {
            @Override
            public void run() {
                Looper.prepare();//創建Looper
                Handler handler = new Handler();//Handler 使用
                Looper.loop();//開啓消息循環
            }
        }.start();

Looper.prepare() 創建 Looper 很簡單不說了,下面有個 Looper.loop() 開啓消息循環,這個是 Looper 的重心啦,看看怎麼實現的

loop() 的源碼比較多,不必每一行都去分析,我們通過剔除一些代碼,讓源碼看起來簡潔一些,這樣就不會掉進源碼的漩渦中不能自拔。

    public static void loop() {
        //剔除代碼
        final MessageQueue queue = me.mQueue;
        //...
        for (;;) {
            Message msg = queue.next(); // 調用 MessageQueue 的 next 方法獲取 Message
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                // msg == null 表示   MessageQueue 正在退出(調用了quit等方法)
                return;
            }
            //剔除代碼
             try {
                // msg.target 就是發送消息的Handler,因此這裏將消息交回 Handler
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            //剔除代碼
        }
    }

前面說了,Looper 通過無限循環的方式獲取 消息隊列 中的消息

for (;;) 死循環從消息隊列中獲取 消息 ,當 msg == null 跳出循環。

通過調用 Looper 的 quit() 或者 quitSafely() 讓 Looper 退出循環

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

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

我們看到 Looper 的 quit() 或者 quitSafely() 方法調用的是 MessageQueuequit() 方法,使得 msg 返回 null 從而讓 Looper 退出循環。這兩個方法的區別是: quit 是直接退出 Looper; quitSafely 會設置一個退出標識,等到隊列中的消息都處理完了,再安全退出。Looper 退出後 Handler 就無法發送消息了

loop() 中 獲取到消息通過 msg.target.dispatchMessage(msg) 將消息 message 交給 Handler 的 dispatchMessage 方法處理,這也就是爲什麼 能夠實現線程切換的關鍵所在,因爲 Handler 運行在主線程

MessageQueue : 獲取消息 next

Looper.loop() 中我們看到通過 MessageQueuenext 方法獲取隊列中的消息

    Message next() {
        // 剔除代碼
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            // 阻塞
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        //存在消息,未到執行時間
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        //存在消息,獲取消息返回 
                        // Got a message.
                        mBlocked = false;//表示目前沒有阻塞
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;//將 msg 從 MessageQueue 鏈表中移除
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // 沒有消息處理 進入阻塞
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

                // MessageQueue 退出 返回 null
                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }
                 //剔除代碼
            }
            //剔除代碼
        }
    }

next 通過 for (;;) 死循環不斷獲取新增的消息,返回給 Looper,如果沒有消息,就一直阻塞在這裏,當有新消息到達,next 會返回這條消息,並且從鏈表中刪除

Handler 工作原理

Handler 的工作原理就比較簡單啦,主要負責 發送消息處理消息

發送消息 主要通過一些列 postsend 方法來實現,具體可以查查接口文檔,通過源碼可以看到最終都是調用 sendMessageAtTime()

    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) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

sendMessageAtTime() 的源碼很簡單,調用 MessageQueue 的方法 enqueueMessage 將 Message 添加到消息隊列,同時 msg.target = this;Handler 自身賦值給了 msg.target

可以發現, Handler 發送的消息過程是向消息隊列添加一條消息,這時 MessageQueue 的 next 方法就會將這條消息返回給 Looper,Looper 收到消息後開始處理,最終通過 msg.target.dispatchMessage(msg); 將消息交給 Handler ,即 Handler 的 dispatchMessage 方法會被調用,這時 Handler 就進入了消息處理階段

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

Handler 處理消息的過程:

首先檢查 Messagecallback 是否爲 null。 不爲 null 直接通過 handleCallback 來處理消息。Messsagecallback 是一個 Runnable 對象,也就是 Handler 的 post 系列方法所傳遞的 Runnable 參數。 handler.post(runnable);
handleCallback

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

然後檢查 mCallback 是否 null。不爲 null 直接通過 mCallbackhandleMessage 處理消息。mCallback 是一個接口,定義如下

    /**
     * Callback interface you can use when instantiating a Handler to avoid
     * having to implement your own subclass of Handler.
     * //不需要通過一個 Handler 的子類,就可以創建一個 Handler 的實例
     *
     * @param msg A {@link android.os.Message Message} object
     * @return True if no further handling is desired
     */
    public interface Callback {
        public boolean handleMessage(Message msg);
    }

接口註釋說明了,通過 Callback 可以不通過 Handler 的子類就能實現一個 Handler 的實例。
不理解?沒事,代碼解釋。開發日常我們用的比較多的是通過繼承 Handler 實現一個 Handler 的子類來實現

class MyHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            //處理消息
        }
    }

而使用 Callback 接口時

Handler handler = new Handler(new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                return false;
            }
        });

當我們不想派生子類時,就可以通過 Callback 的方式實現

OK,再回過頭來看看 Handler 運行在主線程 這個問題。
其實 Handler 運行在哪個線程,是由創建 Handler 的 Looper 決定的,簡單看個例子(onCreate()方法中)

        new Thread("t1") {
            @Override
            public void run() {
                Looper.prepare();
                looper = Looper.myLooper();
                Looper.loop();
            }
        }.start();

        SystemClock.sleep(100);//確保成功獲取到 looper

        Handler handler = new Handler(looper, new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                Log.e("Ruffian", "Thread Name:" + Thread.currentThread().getName());
                return false;
            }
        });
        Message mess = Message.obtain();
        mess.what = 1;
        handler.sendMessage(mess);

解釋一下代碼:創建一個子線程 t1 ,爲這個線程創建 Looper (Looper.prepare();),然後獲取 t1 線程的 Looper 用於創建 Handler

在 Handler 的 handleMessage 中處理消息,打印 Handler 所在的線程,結果爲: t1

那爲什麼默認創建的 Handler 就是運行在主線程的?看下面的例子

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Message mess = Message.obtain();
        mess.what = 1;
        Handler handler = new Handler(new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                Log.e("Ruffian", "Thread Name:" + Thread.currentThread().getName());
                return false;
            }
        });
        handler.sendMessage(mess);
    }

在 Handler 的 handleMessage 中處理消息,打印 Handler 所在的線程,此時,結果爲: main

看一下 Handler 構造方法的代碼(在不指定Looper的時候)

public Handler(Callback callback, boolean async) {
        //剔除代碼
        mLooper = Looper.myLooper();//獲取當前線程Looper
        //剔除代碼
    }

通過代碼可以看出,在創建 Handler 不指定 Looper 時,Handler 的構造方法會獲取當前線程的 Looper ,由於activity 的 onCreate() 方法運行在主線程,所以 Looper 也屬於主線程,通過此 Looper 創建的 Handler 當然也運行在主線程啦~

由此可以驗證 Handler 運行在哪個線程由 創建 Handler 的 Looper 屬於哪個線程決定

講完…

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