目錄:
1. 前言
本篇文章是對(duì) Android HandlerThread 類(lèi)的學(xué)習(xí)邻寿,通過(guò)簡(jiǎn)單的例子槐沼,及分析源碼來(lái)深入學(xué)習(xí)壁晒。同時(shí)例子將以 Java & Kotlin 兩種代碼形式展示。
1.1 定義
HandlerThread: 一個(gè)擁有 Looper 對(duì)象的線(xiàn)程坛怪。
繼承于 Thread 類(lèi)墅拭,并擁有一個(gè) Looper 對(duì)象活玲,可以利用該 Looper 對(duì)象來(lái)創(chuàng)建 Handler 對(duì)象,就像正常的線(xiàn)程一樣谍婉,通過(guò)調(diào)用 start() 方法來(lái)開(kāi)啟線(xiàn)程翼虫。
1.2 使用場(chǎng)景
適用于子線(xiàn)程多任務(wù)的工作場(chǎng)景。如:多個(gè)網(wǎng)絡(luò)請(qǐng)求屡萤,多個(gè)I/O操作
2. 使用方法
其使用方法很簡(jiǎn)單:
- 在主線(xiàn)程中新建一個(gè) HandlerThread 對(duì)象,然后調(diào)用 start() 方法啟動(dòng)該線(xiàn)程掸宛。
- 創(chuàng)建一個(gè)主線(xiàn)程Handler 對(duì)象死陆,關(guān)聯(lián)APP主Looper對(duì)象,用于子線(xiàn)程與主線(xiàn)程之間的溝通唧瘾。
- 創(chuàng)建一個(gè)工作線(xiàn)程Handler 對(duì)象措译,關(guān)聯(lián) HandlerThread 的 Looper 對(duì)象。
- 發(fā)送消息給工作線(xiàn)程饰序,工作線(xiàn)程Handler 接收消息领虹,并處理消息,然后調(diào)用主線(xiàn)程求豫。Handler塌衰,發(fā)送信息給主線(xiàn)程诉稍,通知主線(xiàn)程做UI工作。
- 當(dāng)對(duì)應(yīng)的 Activity 銷(xiāo)毀時(shí)最疆,退出 HandlerThread杯巨,終止消息循環(huán)。
比如:我們用兩個(gè) ProgressBar 來(lái)模擬在子線(xiàn)程中進(jìn)行下載任務(wù)努酸,點(diǎn)擊按鈕開(kāi)始下載服爷,ProgressBar 的進(jìn)度代表著下載進(jìn)度。
如下所示:
2.1 Java版本
具體代碼如下:
public class HandlerThreadActivity extends AppCompatActivity {
private final int SET_PROGRESS_BAR_1 = 1;
private final int SET_PROGRESS_BAR_2 = 2;
private HandlerThread myHandlerThread;
private Handler mWorkHandler, mMainHandler;
private ProgressBar progressBar1, progressBar2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler_thread);
TextView titleTv = findViewById(R.id.title_tv);
titleTv.setText("HandlerThread");
progressBar1 = findViewById(R.id.progress_bar_1);
progressBar2 = findViewById(R.id.progress_bar_2);
Button startBtn1 = findViewById(R.id.start_progress_bar_1_btn);
Button startBtn2 = findViewById(R.id.start_progress_bar_2_btn);
//創(chuàng)建HandlerThread對(duì)象
myHandlerThread = new HandlerThread("myHandlerThread");
//啟動(dòng)線(xiàn)程
myHandlerThread.start();
//創(chuàng)建主線(xiàn)程Handler获诈,關(guān)聯(lián)APP的主Looper對(duì)象
mMainHandler = new Handler(getMainLooper());
//創(chuàng)建工作線(xiàn)程Handler仍源,關(guān)聯(lián)HandlerThread的Looper對(duì)象,所以它無(wú)法與主線(xiàn)程通訊
mWorkHandler = new Handler(myHandlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case SET_PROGRESS_BAR_1:
//設(shè)置Progress Bar 1
for (int i = 1; i <= 4; i++) {
try {
//模擬耗時(shí)工作
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
final int progressSchedule = I;
//在工作線(xiàn)程中舔涎,通過(guò)主線(xiàn)程Handler, 傳遞信息給主線(xiàn)程笼踩,通知主線(xiàn)程處理UI工作。
mMainHandler.post(new Runnable() {
@Override
public void run() {
progressBar1.setProgress(progressSchedule);
}
});
}
break;
case SET_PROGRESS_BAR_2:
//設(shè)置Progress Bar 2
for (int i = 1; i <= 4; i++) {
try {
//模擬耗時(shí)工作
Thread.sleep(1300);
} catch (InterruptedException e) {
e.printStackTrace();
}
final int progressSchedule2 = I;
//在工作線(xiàn)程中终抽,通過(guò)主線(xiàn)程Handler, 傳遞信息給主線(xiàn)程戳表,通知主線(xiàn)程處理UI工作。
mMainHandler.post(new Runnable() {
@Override
public void run() {
progressBar2.setProgress(progressSchedule2);
}
});
}
break;
default:
break;
}
}
};
startBtn1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//通過(guò)工作線(xiàn)程Handler昼伴,mWorkHandler發(fā)送處理 progress bar 1 的信息給工作線(xiàn)程匾旭。
Message msg = new Message();
msg.what = SET_PROGRESS_BAR_1;
mWorkHandler.sendMessage(msg);
}
});
startBtn2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//通過(guò)工作線(xiàn)程Handler mWorkHandler發(fā)送處理 progress bar 2 的信息給工作線(xiàn)程。
Message msg = new Message();
msg.what = SET_PROGRESS_BAR_2;
mWorkHandler.sendMessage(msg);
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
myHandlerThread.quit();
}
}
2.2 Kotlin版本
Kotlin 版本代碼如下:
class TestHandlerThreadActivity : AppCompatActivity(), View.OnClickListener {
var mHandlerThread: HandlerThread? = null
var mMainHandler: Handler? = null
var mWorkHandler: WorkHandler? = null
override fun onClick(v: View?) {
when (v?.id) {
R.id.handlerThreadStart1Btn -> mWorkHandler?.obtainMessage(1)?.sendToTarget()
R.id.handlerThreadStart2Btn -> mWorkHandler?.obtainMessage(2)?.sendToTarget()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test_handler_thread)
mMainHandler = Handler(Looper.getMainLooper())
mHandlerThread = HandlerThread("JereTest")
mHandlerThread!!.start()
mWorkHandler = WorkHandler(mHandlerThread!!.looper)
handlerThreadStart1Btn.setOnClickListener(this)
handlerThreadStart2Btn.setOnClickListener(this)
}
inner class WorkHandler(looper: Looper) : Handler(looper) {
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
when (msg.what) {
1 -> {
for (i in 1..5) {
Thread.sleep(1000)
mMainHandler?.post { handlerThreadProgressBar1.progress = I }
}
}
2 -> {
for (j in 1..5) {
Thread.sleep(1000)
mMainHandler?.post { handlerThreadProgressBar2.progress = j }
}
}
}
}
}
override fun onDestroy() {
super.onDestroy()
mHandlerThread?.quit()
}
}
3. 源碼分析
按使用步驟來(lái)分析源碼:
步驟一:在主線(xiàn)程中新建一個(gè) HandlerThread 對(duì)象圃郊,然后調(diào)用 start() 方法啟動(dòng)該線(xiàn)程价涝。
//創(chuàng)建 HandlerThread 對(duì)象
myHandlerThread = new HandlerThread("myHandlerThread");
//啟動(dòng)線(xiàn)程,執(zhí)行 run() 方法
myHandlerThread.start();
//創(chuàng)建 HandlerThread 對(duì)象持舆,看其構(gòu)造函數(shù)
/**
* HandlerThread的構(gòu)造函數(shù)
* @param name色瘩,線(xiàn)程名字是我們傳入的,用于標(biāo)記改線(xiàn)程
*/
public HandlerThread(String name) {
super(name);
//指定該線(xiàn)程的優(yōu)先級(jí)為標(biāo)準(zhǔn)線(xiàn)程優(yōu)先級(jí)
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
//啟動(dòng)線(xiàn)程
@Override
public void run() {
//獲得線(xiàn)程標(biāo)識(shí)符
mTid = Process.myTid();
//為該線(xiàn)程創(chuàng)建一個(gè) Looper 對(duì)象逸寓,具體見(jiàn)下:
Looper.prepare();
synchronized (this) {
//獲取通過(guò) Looper.prepare() 方法創(chuàng)建的 Looper 對(duì)象居兆。
mLooper = Looper.myLooper();
//喚醒那些為創(chuàng)建Looper對(duì)象而阻塞的線(xiàn)程
notifyAll();
}
//設(shè)置該線(xiàn)程的優(yōu)先級(jí),比如設(shè)置為標(biāo)準(zhǔn)線(xiàn)程優(yōu)先級(jí)
Process.setThreadPriority(mPriority);
//消息循環(huán)前做的處理竹伸,即:如果想在消息循環(huán)前做一些處理泥栖,則需要復(fù)寫(xiě)onLooperPreopared()方法。
onLooperPrepared();
//循環(huán)該Looper對(duì)象中的消息隊(duì)列
Looper.loop();
mTid = -1;
}
步驟二:創(chuàng)建一個(gè)主線(xiàn)程Handler 對(duì)象勋篓,關(guān)聯(lián)APP主Looper對(duì)象吧享,用于子線(xiàn)程與主線(xiàn)程之間的溝通。
//創(chuàng)建主線(xiàn)程Handler譬嚣,關(guān)聯(lián)APP的主Looper對(duì)象
mMainHandler = new Handler(getMainLooper());
其具體源碼分析內(nèi)容請(qǐng)看另外一篇文章Android Handler 深入學(xué)習(xí)及源碼分析
步驟三. 創(chuàng)建一個(gè)工作線(xiàn)程Handler 對(duì)象钢颂,關(guān)聯(lián) HandlerThread 的 Looper 對(duì)象。
其具體實(shí)現(xiàn)方法如下:
//創(chuàng)建工作線(xiàn)程Handler拜银,關(guān)聯(lián)HandlerThread的Looper對(duì)象殊鞭,所以它無(wú)法與主線(xiàn)程通訊
mWorkHandler = new Handler(myHandlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
···略···
}
};
通過(guò) getLooper() 方法獲取 HandlerThread 的 Looper 對(duì)象遭垛,其源碼如下:
/**
* 該方法返回該線(xiàn)程所關(guān)聯(lián)的Looper對(duì)象。
* 如果這個(gè)線(xiàn)程沒(méi)有啟動(dòng)钱豁,或者由于任何原因isAlive()返回false耻卡,那么這個(gè)方法將返回null。
* 如果這個(gè)線(xiàn)程已經(jīng)啟動(dòng)牲尺,這個(gè)方法將阻塞卵酪,直到looper被初始化。
* @return The looper.
*/
public Looper getLooper() {
//線(xiàn)程未激活谤碳,返回 null
if (!isAlive()) {
return null;
}
//如果該線(xiàn)程已經(jīng)啟動(dòng)溃卡,則阻塞該方法,直到創(chuàng)建好Looper對(duì)象蜒简。
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
步驟四:發(fā)送消息給工作線(xiàn)程瘸羡,工作線(xiàn)程Handler 接收消息,并處理消息搓茬,然后調(diào)用主線(xiàn)程Handler犹赖,發(fā)送信息給主線(xiàn)程,通知主線(xiàn)程做UI工作卷仑。
Handler 通過(guò) sendMessage(Msg) 及 post(Runnable) 方法傳遞消息峻村,本篇博客不展開(kāi)具體分析,具體源代碼分析見(jiàn)上一篇博客Android Handler 深入學(xué)習(xí)及源碼分析
步驟五:當(dāng)對(duì)應(yīng)的 Activity 銷(xiāo)毀時(shí)锡凝,退出 HandlerThread粘昨,終止消息循環(huán)。
當(dāng)當(dāng)前 Activity 退出銷(xiāo)毀時(shí)窜锯,復(fù)寫(xiě) onDestry() 方法张肾,退出 HandlerThread,如:
@Override
protected void onDestroy() {
super.onDestroy();
myHandlerThread.quit();
}
通過(guò) quit() 方法退出 HandlerThread锚扎,終止消息循環(huán)吞瞪,具體源代碼分析,見(jiàn)下:
/**
* 終止 Handler 關(guān)聯(lián)的 looper 消息循環(huán)驾孔,不在處理消息隊(duì)列中的任何消息尸饺。
* 當(dāng) looper 被停止后再?lài)L試請(qǐng)求消息入消息隊(duì)列,會(huì)失敗助币,比如:sendMessage(Message) 會(huì)返回 false。
* 使用該方法可能不安全螟碎,因?yàn)?looper 在沒(méi)有循環(huán)分發(fā)完所有消息時(shí)就被停止了眉菱。
* 考慮使用quitsafe() 方法來(lái)代替,以確保所有未完成的工作以有序的方式完成掉分。
*
* @return 如果Looper停止消息循環(huán)俭缓,返回True; 如果線(xiàn)程尚未開(kāi)始運(yùn)行克伊,則返回false。
*/
public boolean quit() {
//獲取 Handler 所關(guān)聯(lián)的 Looper 對(duì)象华坦。
Looper looper = getLooper();
//如果 Looper 不為空愿吹,停止其消息循環(huán),返回true
if (looper != null) {
looper.quit();
return true;
}
//如果 Looper 為空惜姐,即現(xiàn)場(chǎng)還未開(kāi)始運(yùn)行犁跪,返回 false。
return false;
}
/**
* 安全的終止 Handler 關(guān)聯(lián)的 Looper 消息循環(huán)歹袁,處理消息隊(duì)列中所有已到期的消息坷衍,而未到期的掛起的延遲消息將不被傳遞
* 處理完消息隊(duì)列中的所有消息后立即停止 Looper 消息循環(huán)
* 當(dāng) looper 被停止后再?lài)L試請(qǐng)求消息入消息隊(duì)列,會(huì)失敗条舔,比如:sendMessage(Message) 會(huì)返回 false枫耳。
* 如果線(xiàn)程還未開(kāi)始或已經(jīng)結(jié)束(getLooper 返回 null),返回false孟抗;另外迁杨,當(dāng) Looper 被要求 quit() 停止消息循環(huán)時(shí),返回true凄硼。
*
* @return 如果Looper停止消息循環(huán)铅协,返回True; 如果線(xiàn)程尚未開(kāi)始運(yùn)行,則返回false帆喇。
*/
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
//
looper.quitSafely();
return true;
}
return false;
}
至此 HandlerThread 的源碼也就分析結(jié)束了警医。
其實(shí)分享文章的最大目的正是等待著有人指出我的錯(cuò)誤,如果你發(fā)現(xiàn)哪里有錯(cuò)誤坯钦,請(qǐng)毫無(wú)保留的指出即可预皇,虛心請(qǐng)教。
另外婉刀,如果你覺(jué)得文章不錯(cuò)吟温,對(duì)你有所幫助,請(qǐng)給我點(diǎn)個(gè)贊突颊,就當(dāng)鼓勵(lì)鲁豪,謝謝~Peace~!