HandlerThread源碼分析

前言

在Android中,默認情況下,一個線程沒有與它相關聯的Looper。也就是說,默認情況下,我們無法創建與該線程相關聯的Handler。HandlerThread是一個繼承自Thread的線程類,它用於啓動一個有Looper的線程。通過這個Looper,我們就可以創建與該線程相關聯的Handler。通過Handler,我們就可以很方便地控制該線程執行我們的耗時任務。在Android系統中,很多地方都使用了HandlerThread。比如,IntentService、WifiScanningService、NetworkStatsService等等。

基礎知識

HandlerThread的工作原理基於Android的消息機制。具體Android的消息機制的工作原理可以閱讀 Android的消息機制源碼分析 這篇文章。

基本用法

HandlerThread的用法非常簡單。具體的示例代碼如下所示:

HandlerThread thread = new HandlerThread("Thread name");
thread.start();

mLooper = thread.getLooper();
mHandler = new Handler(mLooper) {

    @Override
    public void handleMessage(Message msg) {
        // execute task
    }
};

首先,創建一個HandlerThread線程,並啓動該線程。然後,獲取該線程的Looper,並使用這個Looper來創建一個與該線程相關聯的Handler。最後,通過這個Handler,我們就可以很方便地控制該線程執行我們的耗時任務。我們可以使用發送 Message 的方法,也可以使用投遞 Runnable 的方法。

注意:

  • 在獲取HandlerThread線程的Looper之前一定要記得先啓動線程。
  • 在適當的時候要記得調用mLooper.quit()方法來退出Looper消息循環,結束HandlerThread線程。
  • Looper從MessageQueue中循環地取出Message來處理,所以HandlerThread線程以隊列的方式來執行多個任務。

源碼分析

我們將按照HandlerThread基本用法的順序來進行源碼分析。

首先,我們要創建一個HandlerThread線程。所以,我們先來看HandlerThread的構造方法。

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

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

HandlerThread提供了兩個構造方法。其中,name參數用來設置線程名,priority參數用來設置線程的優先級。第一個構造方法設置HandlerThread線程的優先級爲線程的默認優先級。

然後,我們要啓動該線程。所以,接下來我們來看HandlerThread的run()方法。

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

在run()方法中,首先調用了Looper.prepare()靜態方法來創建一個與該線程相關聯的Looper。Looper.prepare()靜態方法下面的同步代碼塊用來同步該線程的Looper對象。接着設置了該線程的優先級。最後調用了Looper.loop()靜態方法來啓動該線程的消息循環。Looper.loop()靜態方法是一個死循環,它循環地取出MessageQueue中的Message來處理。

接着,我們就可以獲取該線程的Looper了。所以,接下來我們來看HandlerThread的getLooper()方法。

public Looper getLooper() {
    if (!isAlive()) {
        return null;
    }

    // If the thread has been started, wait until the looper has been created.
    synchronized (this) {
        while (isAlive() && mLooper == null) {
            try {
                wait();
            } catch (InterruptedException e) {
            }
        }
    }
    return mLooper;
}

如果在啓動線程之前調用了getLooper()方法,那麼該方法將返回null。如果在啓動線程之後調用了getLooper()方法,那麼該方法將阻塞,直到Looper對象創建完畢。所以,我們要先啓動HandlerThread線程,然後再通過getLooper()方法來獲取與該線程相關聯的Looper。獲取到Looper之後,我們就可以創建與該線程相關聯的Handler了。通過Handler,我們就可以很方便地控制該線程執行我們的耗時任務。

最後,在適當的時候我們要調用mLooper.quit()方法來退出Looper消息循環,結束HandlerThread線程。所以,最後我們來看HandlerThread的quit()方法。

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

quit()方法調用了Looper的quit()方法來退出Looper.loop()死循環,結束HandlerThread線程。

總結

HandlerThread的工作原理基於Android的消息機制。通過使用HandlerThread線程,我們可以很方便地控制該線程執行我們的耗時任務。

例子

這裏舉一個簡單的例子來實踐HandlerThread。項目源碼地址:https://github.com/chongyucaiyan/HandlerDemo

我們創建了一個HandlerThread線程,並創建了一個與它相關聯的Handler。通過這個Handler,我們發送Message來控制HandlerThread線程執行耗時任務。具體的代碼和註釋如下所示:

public class MainActivity extends AppCompatActivity {
    private static final int DO_TASK1 = 1;
    private static final int DO_TASK2 = 2;

    private Looper mLooper;
    private MyHandler mHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 創建HandlerThread線程
        createHandlerThread();
    }

    private void createHandlerThread() {
        // 創建HandlerThread線程,並啓動該線程
        HandlerThread thread = new HandlerThread("HandlerThread");
        thread.start();
        // 獲取線程的Looper,創建與該線程相關聯的Handler
        mLooper = thread.getLooper();
        mHandler = new MyHandler(mLooper);
        // 發送任務消息
        sendMessage();
    }

    private void sendMessage() {
        Message message1 = mHandler.obtainMessage(DO_TASK1);
        Message message2 = mHandler.obtainMessage(DO_TASK2);
        mHandler.sendMessage(message1);
        mHandler.sendMessage(message2);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 退出Looper消息循環
        mLooper.quit();
    }

    private static class MyHandler extends Handler {
        private static final String TAG = "MyHandler";

        public MyHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case DO_TASK1:
                    // 執行任務1
                    doTask1();
                    break;
                case DO_TASK2:
                    // 執行任務2
                    doTask2();
                    break;
                default:
                    break;
            }
        }

        private void doTask1() {
            Log.i(TAG, "doTask1(), start");
            Log.i(TAG, "doTask1(), thread name is " + Thread.currentThread().getName());

            // 模擬耗時任務
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            Log.i(TAG, "doTask1(), end");
        }

        private void doTask2() {
            Log.i(TAG, "doTask2(), start");
            Log.i(TAG, "doTask2(), thread name is " + Thread.currentThread().getName());

            // 模擬耗時任務
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            Log.i(TAG, "doTask2(), end");
        }
    }
}

運行應用,打印的信息如下圖所示:

任務執行打印的信息.png

可以看到:

  1. 任務是在HandlerThread子線程中執行的。
  2. 任務2是在任務1執行完畢之後纔開始執行的。這驗證了HandlerThread線程是以隊列的方式來執行多個任務的。

參考

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