HandlerThread

話說某一天去游族面試,被問到知不知道HanderThread,當時就懵逼了,梗了半天回答不出來,沒有了解過...
結果,結果成功被刷~
其實翻了下源碼發現挺簡單的,這玩意其實就是一個Thread子類并且幫你創建一個Looper并開了Looper循環,我們之前一般用Handler都是習慣在UI線程中去new個Handler然后在子線程中去sendMessage這樣就可以子線程執行耗時操作然后在UI線程中跟新UI。
但有時我們可能需要在子線程中做耗時操作然后在子線程中跟新UI,別跟我說不可能,SurfaceView?
這樣的話我們的Looper就不能在UI線程中去接收Message消息了。
這種操作一般有幾種做法.
1.我們在子線程中利用while或者for循環重復去做耗時跟新操作
2.在子線程中創建一個Looper然后調用Looper prapre() 和 loop()遍歷和發送消息

第一種操作先不去探究,而HandlerThread就是對第二種方式簡易的封裝,讓實現變的更容易一些

我們看看怎么使用的

        HandlerThread handlerThread=new HandlerThread("new_thread1");
        handlerThread.start();
        Handler handler=new Handler(handlerThread.getLooper()){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
            }
        };

是不是感覺簡單到不可思議居然就這么幾行代碼就可以了?不錯這樣就算Handler在主線程中new出來的,那么它處理消息也在我們創建的new_thread1這個子線程中去執行,如果你也感覺到很好奇那么我們就分析一下源碼,在分析之前,先回憶一下Handler Looper Message之間的知識,簡單回憶下,我們知道,Handler之所以可以在UI線程中處理消息因為它在UI線程中new出來的,在new出來過程中會從UI線程中的threadlocal中取出UI線程創建Looper對象存于成員變量,這樣不管我們在哪個子線程調用handler發送Message那么都是發送到UI線程中Looper中的MessageQueue隊列中,這個Looper是在UI線程中調用loop()阻塞遍歷隊列消息的,接受到消息后調用message里的handler的dispatchMessage()去處理消息所以一定發生在ui線程中

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

而Handler有個構造接收一個Looper參數最終會調用三參構造,這個構造中會把Looper賦值成員變量,我們在上面操作HandlerThread時是獲取它的Looper然后賦值給Handler成員變量的,那么它的Looper在哪創建的呢,其實很簡單就在重寫的run()方法里
不信我們翻翻

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

看到了吧,其中有個 onLooperPrepared();方法是個空實現方法我們可以重寫它在loop()方法調用之前進行一些準備工作,其中有個小知識點需要注意下,雖然我們開始調用了start()開啟了一個新線程并且在run()方法中創建了Looper但是由于線程之間的執行次序是不確定的,所以我們需要等子線程中的Looper對象完全創建完畢后才能通過getLooper()拿到這個對象,所以在getLooper()方法中進行了一個判空wait()狀態

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

而在上面當Looper.prepare()調用完畢Looper對象創建完畢調用notifyAll喚醒了前面wait()的線程

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容