上一篇我們通過源碼分析了Handler的消息流程原理,如果對(duì)handler的原理還不夠明白的同學(xué)可以先學(xué)習(xí)上篇。我們今天的主角是HandlerThread。此乃我android大軍一員猛將也。
目錄
- HandlerThread簡(jiǎn)單介紹
- 如果沒有Handler和HandlerThread以前
- HandlerThread源碼分析
- 原生線程間通信
HandlerThread
從名字上來看,這廝肯定和線程有扯不開的關(guān)系。只聞起名還未使用過的同學(xué)別擔(dān)心,我們先說說他的做用,再分析源碼的實(shí)現(xiàn)。
先思考這樣一個(gè)場(chǎng)景,我們知道在android中主線程中是不能做復(fù)雜的耗時(shí)操作。然而可不可以有一種機(jī)制是主線程通知子線程來做某件事呢?
注意: 這里說的是通知子線程來做某件事,不是說在主線程中另起一個(gè)子線程來做某件事。兩者是有區(qū)別的。
這個(gè)還真可以有,并且HandlerThread就是做這件事情的。
首先的確HandlerThread本身就是一個(gè)線程,他的設(shè)計(jì)可以用來將事件流在不同的線程中進(jìn)行切換。恰巧Android主線程可以利用它來做一些耗時(shí)的操作。
如果沒有Handler和HandlerThread以前
我們先來看一個(gè)沒有這種機(jī)制以前是怎么處理的。
下面是偽代碼:
Thread mTh1 =new Thread(()->{
·····
//我做完了,現(xiàn)在想通知mainTh怎么辦???
});
Thread mainTh=new Thread(()->{
//做一些事情
·····
//做完了調(diào)用
mTh1.start();
});
mainTh.start(); //開始做事
這段代碼很簡(jiǎn)單,我們有兩個(gè)線程,mainTh做完某些事情以后將啟動(dòng)子線程mTh1來執(zhí)行。這些都沒有問題,但當(dāng)mTh1完成任務(wù)以后,它想再次回到主線程中告知mainTh怎么辦?是不是思路瞬間短路了。
為什么?因?yàn)榫€程總是順序執(zhí)行的,而且是并行順序執(zhí)行,一旦執(zhí)行就沒有回路。
當(dāng)HandlerThread出現(xiàn)以后
準(zhǔn)確來說是當(dāng)Handler、Looper、Message、MessageQueue出現(xiàn)以后,HandlerThread基于此。再次提醒讀者先弄清楚Handler通信機(jī)制才能搞明我們下面要分析的。
還是上面這個(gè)功能,我們看偽代碼。
protected void onCreate(Bundle savedInstanceState) {
mHThread = new HandlerThread( "mHThread") ;
mHThread .start();
mMainHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
//這里收到的消息將會(huì)在主線程中執(zhí)行
}
};
mHandler = new Handler(mHThread.getLooper()){
@Override
public void handleMessage(Message msg) {
//這里收到的消息將會(huì)在mHThread子線程中處理
·····
//我做完了,現(xiàn)在將消息通知回主線程
mMainHandler.sendEmptyMessage(2) ;
}
};
//主線程做一些事情
········
//做完了調(diào)用mHandler通知子線程mHThread
mHandler.sendEmptyMessage(1) ;
}
沒毛病,我們?cè)?code>Activity的onCreate方法中創(chuàng)建了:
- 兩個(gè)
Handler - 一個(gè)
HandlerThread
我們前面說過HandlerThread其實(shí)就是一個(gè)線程。其中mMainHandler使用了無參構(gòu)造函數(shù),那么它將獲取主線程中的Looper(見Handler源碼)。mHandler傳入了mHThread.getLooper(),即HandlerThread中的子線程中的Looper。兩個(gè)Handler分別持有了主線程的Looper和子線程的Looper。重點(diǎn)就在此處。
然后呢,主線程使用mHandler發(fā)送消息給HandlerThread的Looper進(jìn)行處理。此時(shí)這件事交由子線程來完成。隨后子線程做完事情以后將調(diào)用mMainHandler來通知主線程中的Looper完成主線程中該有的操作。至此順利進(jìn)行了一把線程之間的通信。
HandlerThread源碼分析
為了不占用篇幅,下面的HandlerThread源碼是我精簡(jiǎn)過的,本身源碼也不復(fù)雜。讀者可以自行打開as查看。
public class HandlerThread extends Thread {
Looper mLooper;
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();//創(chuàng)建Looper
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();//開啟消息輪訓(xùn)
mTid = -1;
}
public Looper getLooper() { return mLooper;}
public boolean quit() { looper.quit(); }
public boolean quitSafely() { looper.quitSafely(); }
}
我們可以看到HandlerThread繼承了Thread所以本質(zhì)上它就是一個(gè)普通的線程。那么其中run方法里創(chuàng)建Looper并開啟了循環(huán)隊(duì)列。整個(gè)源碼極其簡(jiǎn)單。前面我的使用場(chǎng)景中就是通過了getLooper方法來獲取當(dāng)前線程中的Looper,所以handler才能在在線程之間將消息靈活的處理。
原生線程間通信
略提一下,拋開Handler在原生Java中其實(shí)也有辦法做線程間通信。只是方法要么不夠優(yōu)雅,要么會(huì)造成cpu運(yùn)算負(fù)荷超高或死鎖等情況。而且經(jīng)常因?yàn)榧夹g(shù)不到位,導(dǎo)致翻車的情況。所以android中的Hanlder通信機(jī)制非常巧妙的避開這樣的問題,提供一下原生方法參考學(xué)習(xí)。
- 共享內(nèi)存變量 使用同步鎖(容易死鎖)
- 共享內(nèi)存變量 判斷變量狀態(tài) (極其消耗內(nèi)存)
- 管道通信
題外話
給各位正在進(jìn)階的同學(xué)們提個(gè)醒,現(xiàn)在大部分工作中都在使用優(yōu)雅的輪子來解決線程切換的問題如rxJava、rxAndroid等,確實(shí)它們非常好用和方便,但不代表handler這種原生的對(duì)象就可以擯棄。那些優(yōu)秀框架的實(shí)現(xiàn)都離不android原生的特性,如果要往上進(jìn)階學(xué)習(xí),就不能只是每天看看這里的大神新出一個(gè)輪子,那里大廠又開源一個(gè)框架,問其原理一概不知,自己去看他們的源碼也是云霧朦朧的。這樣以來自己的技術(shù)深度很難有較大的進(jìn)步。而如果是先從基礎(chǔ)開始早一點(diǎn)摸清他們的套路,再去看人家的框架是怎么寫的。學(xué)習(xí)那些新東西是很快的。而我自己就曾經(jīng)犯下這樣的錯(cuò)誤,走了很多彎路。希望能給各位同學(xué)帶來幫助。愿君與共勉。
下一篇我們將分析IntentService的源碼。
如何下次找到我?
- 關(guān)注我的簡(jiǎn)書
- 本篇同步Github倉庫:https://github.com/BolexLiu/DevNote (可以關(guān)注)
