面試總結(jié)(1):HandlerThread

前言#

面試總是讓人既緊張又興奮,尤其是百度這樣大公司,總是想證明自己的能力,害怕但是又期望能問出自己的不足,真是一次難得面試體驗,所以回來趕緊把沒回答出來的知識點學(xué)習(xí)一下。

Handler和Thread之間的關(guān)系和用法,我們都很熟悉,那么你了解HandlerThread嗎?

正文#

當(dāng)我聽到這個問題的時候,是有一點蒙的,因為我確實沒用過,可能是平時看到或者聽到過。那什么是HandlerThread呢?

顧名思義,就是一個含有Handler的Thread,他是一個線程。子線程就可以用用來處理異步任務(wù),耗時操作。

用法:

HandlerThread teacherThread = new HandlerThread("teacher");
teacherThread.start();
teacherHandler = new Handler(teacherThread.getLooper());

很簡單,創(chuàng)建一個HandlerThread對象,然后把這個線程的Looper綁定到Handler里面去,那么Handler里的任務(wù)就會在這個Thread中執(zhí)行。

<h2>好處</h2>

經(jīng)過我仔細的思考和試驗,主要有以下幾點:

1、模塊劃分清晰,把耗時任務(wù)細分,便于維護管理。
2、復(fù)用性很好,避免創(chuàng)建臨時性的線程,消耗系統(tǒng)資源。
3、有利于多線程通信。
4、讓UIhandler更專注于更新界面,優(yōu)化界面的流暢度。

<h2>Demo</h2>

多說無用,趕緊寫點東西實際感受一下。

我們來模擬一個考場的情景:

/**
* 線程池
 * */
private ExecutorService threadPool = Executors.newSingleThreadExecutor();

/**
* 更新UI的Handler
* */
private Handler mainHandler = new Handler(Looper.getMainLooper()){
   @Override
   public void handleMessage(Message msg) {
      switch (msg.what){
         case 0:
             textView.setText("考試開始,老師開始發(fā)放試卷...");
             break;
         case 1:
             textView.setText("發(fā)放試卷完畢,學(xué)生開始答題...");
             break;
         case 2:
             textView.setText("考試時間到,老師收試卷...");
             break;
         case 3:
             textView.setText("考試結(jié)束...");
             break;
         }
   }
};

/**
* 線程池開啟任務(wù)
*/
threadPool.execute(new Runnable() {
    @Override
    public void run() {
       mainHandler.sendEmptyMessage(0);
       // 老師發(fā)卷
       ...
       mainHandler.sendEmptyMessage(1);
       // 學(xué)生答題
       ...
       mainHandler.sendEmptyMessage(2);
       // 答題結(jié)束
       ...
       mainHandler.sendEmptyMessage(3);
    }
});

大概就是這樣一個流程,如果是一般寫法,首先創(chuàng)建一個線程池開啟線程,線程里面開始處理各種各樣的任務(wù),到一定的階段,就更新UI的顯示狀態(tài)。

但是如果這個線程任務(wù)變得越來越復(fù)雜,代碼越來越長,維護性也會變差,所以就得重新設(shè)計一下這個任務(wù)。

從面向?qū)ο蟮木幊趟枷雭砜?,首先我們可以把這個任務(wù)劃分為兩個角色:

老師:發(fā)送試卷,回收試卷。
學(xué)生:答題。

老師發(fā)送完試卷,通知學(xué)生答題,學(xué)生答題結(jié)束,通知老師回收試卷。

ok,分析結(jié)束,就先實現(xiàn)老師和學(xué)生的Handler:

teacherHandler = new Handler(teacherThread.getLooper()){
     @Override
     public void handleMessage(Message msg) {
         switch (msg.what){
              // 接收到開始指令,老師開始發(fā)放試卷
              case 0:
                 // 更新UI
                 mainHandler.sendEmptyMessage(0);
                 sleep();
                 // 發(fā)送試卷結(jié)束,學(xué)生開始答題
                 studentHandler.sendEmptyMessage(0);
                 break;

                    // 學(xué)生答題結(jié)束,老師開始收試卷
              case 1:
                 // 開始收卷
                 mainHandler.sendEmptyMessage(2);
                 sleep();
                 // 考試結(jié)束
                 mainHandler.sendEmptyMessage(3);
                 break;
         }
    }
};

studentHandler = new Handler(studentThread.getLooper()){
   @Override
   public void handleMessage(Message msg) {
       // 接收到開始指令,老師開始發(fā)放試卷
       mainHandler.sendEmptyMessage(1);
       sleep();
       // 發(fā)送試卷結(jié)束,學(xué)生開始答題
       teacherHandler.sendEmptyMessage(1);
    }
};

// mainHandler 代碼不變
...

老師和學(xué)生通過handleMessage()來接受操作指令,內(nèi)部實現(xiàn)具體的功能邏輯。

搞定了兩個角色類,剩下的就是onCreate中去初始化線程了:

public class MainActivity extends AppCompatActivity {

    private TextView textView;

    /**
     * 處理老師和學(xué)生操作的線程
     * */
    private HandlerThread teacherThread, studentThread;

    private Handler teacherHandler, studentHandler;
    
    /**
     * 更新UI的Handler
     * */
    private Handler mainHandler = new Handler(Looper.getMainLooper()){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case 0:
                    textView.setText("考試開始,老師開始發(fā)放試卷...");
                    break;
                case 1:
                    textView.setText("發(fā)放試卷完畢,考試開始答題...");
                    break;
                case 2:
                    textView.setText("考試時間到,老師收試卷...");
                    break;
                case 3:
                    textView.setText("考試結(jié)束...");
                    break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById(R.id.textView);

        initHandlerThread();

        teacherHandler.sendEmptyMessage(0);
    }

    /**
     * 初始化HandlerThread
     * */
    private void initHandlerThread() {
        Log.e("lzp", "mainLooper:" + getMainLooper().toString());

        teacherThread = new HandlerThread("teacher");
        teacherThread.start();
        Log.e("lzp", "teacherLooper:" + teacherThread.getLooper().toString());
        teacherHandler = new Handler(teacherThread.getLooper()){
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what){
                    // 接收到開始指令,老師開始發(fā)放試卷
                    case 0:
                        // 更新UI
                        mainHandler.sendEmptyMessage(0);
                        sleep();
                        // 發(fā)送試卷結(jié)束,學(xué)生開始答題
                        studentHandler.sendEmptyMessage(0);
                        break;

                    // 學(xué)生答題結(jié)束,老師開始收試卷
                    case 1:
                        // 開始收卷
                        mainHandler.sendEmptyMessage(2);
                        sleep();
                        // 考試結(jié)束
                        mainHandler.sendEmptyMessage(3);
                        break;
                }


            }
        };

        studentThread = new HandlerThread("student");
        studentThread.start();
        Log.e("lzp", "studentLooper:" + studentThread.getLooper().toString());
        studentHandler = new Handler(studentThread.getLooper()){
            @Override
            public void handleMessage(Message msg) {
                // 接收到開始指令,老師開始發(fā)放試卷
                mainHandler.sendEmptyMessage(1);
                sleep();
                // 發(fā)送試卷結(jié)束,學(xué)生開始答題
                teacherHandler.sendEmptyMessage(1);
            }
        };
    }

    private void sleep(){
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

為了便于顯示,我沒有把teacherHandler和studentHandler 兩個類獨立出來,如果獨立出來,MainActivity的代碼將會變得更加簡潔易看。

我還打印了UI線程的Looper,和兩個HandlerThread的Looper:

這里寫圖片描述

三個線程的Looper是不一樣的,的確我們的teacherHandler和studentHandler是運行在新線程中的。而且老師和學(xué)生之間的通信變得很簡單,直接通過Message把需要的數(shù)據(jù)攜帶進去就OK了。

使用結(jié)束記得退出HandlerThread:

public void release(){
   getLooper().quit();
}

總結(jié)#

HandlerThread的使用方法就是這個樣子,他不需要我們?nèi)Whread的管理,把更多的精力放在實現(xiàn)handler的功能上,并且handler最大的優(yōu)勢就是便于線程之間的通信,上面的例子我覺得如果按照實際開發(fā)可能并不是很恰當(dāng),相信在以后真真正正的使用到了HandlerThread,我對他的理解會更加清晰。

我對demo重新修改了一下,盡可能的讓代碼維護性提高,耦合性降低,需要的朋友可以下載。

Demo下載鏈接

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容