HandlerThread原理分析、實戰、最佳實踐!

本文我們將學習HandlerThread的實現原理,以及開發時,如何正確的使用它來實現我們的開發任務。

HandlerThread源碼分析


設想這樣一個場景:我們要在一個線程A中處理業務邏輯,在另一個線程B中,監聽A的執行,並進行結果處理。這時我們使用HandlerThread就可以非常簡單的實現該功能了。

通常我們的線程交互場景,都是UI線程中啓動子線程,並且由子線程完成工作任務,最終結果交給UI線程。現在我們的使用場景是,在子線程中監控其他線程的執行結果(這裏的其他線程可以是另一個子線程,也可以是UI線程),並在子線程中進行結果的處理。

通過描述,我們可以得出2點結論:第一,這個過程中,需要存在2個線程;第二,這兩個線程需要進行數據傳輸(交互)。那麼,我們很自然的就想到了Handler機制來實現該功能,但是我們自己在一個子線程中,使用Handler稍顯麻煩一些,HandlerThread內置了Handler,簡化了我們的操作。

HandlerThread構造函數和繼承關係:

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

    public HandlerThread(String name) {//構造函數1
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }
    
    /**
     * Constructs a HandlerThread.
     * @param name
     * @param priority The priority to run the thread at. The value supplied must be from 
     * {@link android.os.Process} and not from java.lang.Thread.
     */
    public HandlerThread(String name, int priority) {//構造函數2
        super(name);
        mPriority = priority;
    }
}
邏輯解析:
  1. HandlerThread繼承自Thread類,所以它本質上是一個線程類,可以實現線程相關的操作。
  2. 我們來看HandlerThread的構造函數,這裏有2個重載版本。
  3. 第一個構造函數接收一個name參數,直接調用了super方法,爲該線程命令,且線程優先級爲Process.THREAD_PRIORITY_DEFAULT。
  4. 第二個構造函數接收2個參數,可實現線程命名,和設置線程的優先級。

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;
    }
邏輯解析:
  1. 我們在創建了HandlerThread實例之後,調用start()方法執行,ART虛擬機會幫我們創建一個線程對象,然後在子線程中執行run方法。
  2. run方法中,執行了Looper.prepare()方法,創建了一個Looper對象並綁定到該線程。
  3. 然後在同步塊中,執行了mLooper的賦值,調用notifyAll通知Looper已創建完成。
  4. 調用Process.setThreadPriority方法設置線程優先級。
  5. 調用空方法onLooperPrepared,通常用於回調使用。
  6. 最後調用Looper.loop()方法,啓動該線程的消息循環。

getLooper方法:

    public Looper getLooper() {
        if (!isAlive()) {//判斷線程是否存活
            return null;
        }
        
        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            //如果線程存活,但mLooper沒有創建完成,則等待
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;//返回當前線程的Looper對象
    }
邏輯解析:
  1. getLooper方法可以返回當前線程綁定的Looper對象,我們可以使用該對象作爲參數,創建一個該線程的Handler對象,用於線程交互。
  2. getLooper方法首先會判斷當前線程是否存活,如果存活,則繼續。
  3. 如果線程存活,但Looper對象還沒有創建完成,則調用wait方法進行等待(Looper創建完成後,會在HandlerThread的run方法中,調用notifyAll()通知,以繼續執行當前邏輯)。
  4. 最後返回Looper對象即可。

HandlerThread的getThreadHandler方法

    /**
     * @return a shared {@link Handler} associated with this thread
     * @hide
     */
    public Handler getThreadHandler() {
        if (mHandler == null) {
            mHandler = new Handler(getLooper());
        }
        return mHandler;
    }
  1. 如果我們不想自己創建Handler對象,也可以使用HandlerThread爲他們提供的Handler對象。
  2. getThreadHandler方法返回一個當前線程的Handler對象。
  3. 它是一個隱藏方法,我們在應用中不可調用。

HandlerThread的退出

    //立即執行退出
    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;
    }

當不需要HandlerThread時,需要調用quit方法或quitSafely方法結束線程管理的Looper消息循環。

HandlerThread實戰


HandlerThread的實現其實並不複雜,我們以一個簡單Demo來看它的使用。

Demo

public class HandlerThreadTest {
    private final static String TAG = "budaye";
    public static final int HT_MSG = 1;
    private HandlerThread mHandlerThread = new HandlerThread("myHandlerThread", Process.THREAD_PRIORITY_BACKGROUND);
    private Handler mHandler = null;

    public void startHandlerthread(){
        mHandlerThread.start();
        if (mHandler == null){
            mHandler = new Handler(mHandlerThread.getLooper()){
                @Override
                public void handleMessage(@NonNull Message msg) {
                    super.handleMessage(msg);
                    switch (msg.what){
                        case HT_MSG:
                            Log.d(TAG, "當前線程:" + Thread.currentThread());
                            Log.d(TAG, "收到其他線程發送過來的消息了");
                            break;
                    }
                }
            };
        }
    }
    public void sendMessage() {
        Log.d(TAG, "當前線程:" + Thread.currentThread());
        Message msg = Message.obtain();
        msg.what = HT_MSG;
        mHandler.sendMessage(msg);
    }
}
邏輯解析:
  1. 在HandlerThreadTest裏,創建了一個HandlerThread對象。
  2. 調用startHandlerthread方法,開始了HandlerThread對象的線程消息循環,並且創建了一個mHandler對象,用於處理其他線程發送過來的消息。
  3. 我們可以在UI線程中調用sendMessage方法來發送給HandlerThread線程一個消息,並在mHandler對象的handleMessage方法中進行處理。

Demo日誌輸出

25208-25208/com.example.simpledemo D/budaye: 當前線程:Thread[main,5,main]
25208-25284/com.example.simpledemo D/budaye: 當前線程:Thread[myHandlerThread,5,main]
25208-25284/com.example.simpledemo D/budaye: 收到其他線程發送過來的消息了

我們分別在2個線程中輸出了日誌。

最佳實踐&總結


  1. HandlerThread是一個異步處理的工具類,它可以幫助我們很輕鬆的實現異步線程處理。
  2. HandlerThread繼承自Thread類,它的本質是一個線程類。
  3. HandlerThread實現原理非常簡單,它利用了Handler原理,在內部了一個Looper循環,並綁定到當前線程中。
  4. 我們使用創建一個Handler對象,綁定到HandlerTHread對象所對應的Looper,並處理其他線程發送過來的消息。
  5. HandlerThread在構造方法中可以設置線程優先級,默認使用Process.THREAD_PRIORITY_DEFAULT作爲默認優先級。
  6. 我們在應用過程中,不要大量使用HandlerThread來執行異步任務,這樣會造成線程資源的浪費,大量的異步任務,建議使用線程池來進行操作。
  7. HandlerThread的線程優先級設定一定要注意,如果任務優先級不高,則應該設置成後臺任務優先級,避免和UI線程搶佔系統資源。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章