Android多線程之HandlerThread


@author:小馬快跑
@email:[email protected]
@github:https://github.com/crazyqiang


HandlerThread的介紹及用法

HandlerThread繼承自Thread,內部實現了初始化了Looper,並創建了消息隊列,接着調用了Looper.loop()開啓了消息循環,這樣HandlerThread就可以處理通過Handler傳遞過來的Message了,因爲HandlerThread中的run方法是無限循環,當有消息過來時處理消息,沒有消息時就會阻塞。當明確不需要HandlerThread時,可以調用quit或者quitSafely (API 18以上使用)來嘗試終止線程。

先看實現的一個效果圖:

HandlerThread.gif
完整代碼已上傳github:Android多線程之HandlerThread

來分析實現代碼,先定義對象:

 private HandlerThread handlerThread;
 private Handler mainHandler;
 private Handler uiHandler;

初始化:

 handlerThread = new HandlerThread("handler_thread");
 handlerThread.start();
 //在主線程初始化,傳入的是HandlerThread中的Looper
 mainHandler = new Handler(handlerThread.getLooper()) {
     @Override
     public void handleMessage(Message msg) {
         //Handler是在HandlerThread所在的子線程線程中處理Message
         switch (msg.what) {
             case MSG_UPDATE:
                 if (isStop) return;
                 try {
                     //更新UI
                     String result = String.valueOf(new Random().nextInt(9000) + 1000);
                     sendMsg(UI_UPDATE, result, uiHandler);
                     //延遲2秒
                     Thread.sleep(2000);
                     //循環發送消息
                     mainHandler.sendEmptyMessage(MSG_UPDATE);
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
                 break;
         }
     }
 };

首先,初始化HandlerThread並通過handlerThread.getLooper()關聯一個在UI線程初始化的mainHandler,這樣就可以通過mainHandler在主線程中向HandlerThread中發送消息了,這裏要注意一下,mainHandler中的handleMessage()方法是在HandlerThread子線程中執行的,爲什麼會在子線程中執行呢?因爲在初始化mainHandler時傳入的是HandlerThread的Looper,而mainHandler是把消息發送到HandlerThread中去,HandlerThread在執行Looper.loop()方法後會循環取出消息並處理消息,所以mainHandler中的handleMessage()方法是在HandlerThread子線程中執行的,那麼處理完消息後怎麼更新到UI線程呢?

 //在主線程初始化,傳入的是主線程中的Looper
 uiHandler = new Handler(Looper.getMainLooper()) {
     @Override
     public void handleMessage(Message msg) {
         //在主線程中處理Message
         switch (msg.what) {
             case UI_UPDATE:
                 //更新驗證碼
                 String result = (String) msg.obj;
                 tv_random.setText(result);
                 break;
         }
     }
 };

我們在主線程中初始化了另一個uiHandler 並傳入了主線程的Looper,所以此時最後處理消息的回調方法handleMessage()是在主線程中執行的,當HandlerThread處理完邏輯後,通過uiHandler把結果發到主線程中,就可以愉快地來更新UI了,最後別忘了關閉線程:

@Override
 protected void onDestroy() {
     if (SDK_INT >= 18) {
         handlerThread.quitSafely();
     } else {
         handlerThread.quit();
     }
     super.onDestroy();
 }

HandlerThread API

返回結果 HandlerThread 備註
Looper getLooper() 返回和HandlerThread關聯的Looper
int getThreadId() 返回HandlerThread線程的標識符,參見Process.myTid()
boolean quit() 立即停止Looper的循環,即使messageQueue中有消息也不再處理,在調用此方法後,任何傳遞Message的方法 (如sendMessage(Message)) 都將失敗並返回false
boolean quitSafely() 當messageQueue中沒有Message後,在調用此方法後立即終止Looper,任何傳遞Message的方法 (如sendMessage(Message)) 都將失敗並返回false
void run() 如果HandlerThread使用單獨的Runnable來構造,將執行此方法

HandlerThread源碼分析

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

    //初始化HandlerThread
    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }

   //初始化HandlerThread
   public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }

    protected void onLooperPrepared() {
    }

    @Override
    public void run() {
        mTid = Process.myTid();
        //調用Looper.prepare()初始化Looper 並把Looper放到sThreadLocal中,sThreadLocal可以在不同線程中保存對象副本
        //在Looper的構造方法中就初始化了一個messageQueue,用來存放傳入的消息
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        //開始循環從messageQueue中取出消息並處理消息,無消息會阻塞,有消息來時就會喚醒
        Looper.loop();
        mTid = -1;
    }

    //返回的即是run方法中產生的Looper
    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的循環,即使messageQueue中有消息也不再處理,在調用此方法後,任何傳遞Message的
    //方法 (如sendMessage(Message)) 都將失敗並返回false
    public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }

    //當messageQueue中沒有Message後,在調用此方法後立即終止Looper,任何傳遞Message的
    //方法 (如sendMessage(Message)) 都將失敗並返回false
    public boolean quitSafely() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quitSafely();
            return true;
        }
        return false;
    }

    public int getThreadId() {
        return mTid;
    }
}

源碼很簡單,首先HandlerThread繼承了Thread,所以HandlerThread本質上還是一個線程,只不過在run()方法中又初始化了Looper和MessageQueue,這樣當外界通過Handler向HandlerThread傳入消息後,HandlerThread就會取出消息並處理消息,當沒有消息時就會阻塞。

發佈了53 篇原創文章 · 獲贊 98 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章