前言#
面試總是讓人既緊張又興奮,尤其是百度這樣大公司,總是想證明自己的能力,害怕但是又期望能問出自己的不足,真是一次難得面試體驗,所以回來趕緊把沒回答出來的知識點學(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)Whread的管理,把更多的精力放在實現(xiàn)handler的功能上,并且handler最大的優(yōu)勢就是便于線程之間的通信,上面的例子我覺得如果按照實際開發(fā)可能并不是很恰當(dāng),相信在以后真真正正的使用到了HandlerThread,我對他的理解會更加清晰。
我對demo重新修改了一下,盡可能的讓代碼維護性提高,耦合性降低,需要的朋友可以下載。