簡介
首先我們先來了解HandlerThread和IntentService是什么,以及為什么要將這兩者放在一起分析槽畔。
HandlerThread:
HandlerThread 其實是Handler + Thread + Looper的組合骤视,它本質上是一個Thread鞍爱,因為它繼承了Thread。相比普通的Thread专酗,它不會堵塞睹逃,因為他內部通過Looper實現(xiàn)了消息循環(huán)機制,保證了多個任務的串行執(zhí)行祷肯。缺點是效率比較低沉填,因為,串行執(zhí)行比起并行執(zhí)行佑笋,效率肯定會比較較低翼闹。
IntentService:
IntentService是繼承并處理異步請求的一個類,其本質上是一個Service蒋纬,因為他繼承了Service猎荠,所以開啟IntentService和普通的Service一致。但是他和普通的IntentService不同之處在于颠锉,他可以處理異步任務法牲,在任務處理完成之后會自動結束Service。另外我們可以啟動IntentService多次琼掠,而每一個耗時任務會已工作隊列的方式在IntentService的onHandleIntent回調方法中執(zhí)行拒垃,并且是串行執(zhí)行的。
好了在了解HandlerThread和IntentService分別是什么之后瓷蛙,我們來解決第二個問題悼瓮,那就是為什么我要將2者放在一起分析?其實IntentService的內部是通過HandlerThread和Handler來實現(xiàn)異步操作的艰猬,當我們了解了HandlerThread的使用和原理之后横堡,再去理解IntentService就會容易的多。好的冠桃,下面讓我們開始HandlerThread的源碼之旅命贴。
HandlerThread的使用和原理
HandlerThread的使用
這里我們要實現(xiàn)一個每隔5s更新TextView中的值的一個demo,源碼如下:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private TextView mTv;
private Button mBtn;
HandlerThread mHandlerThread;
Handler mThreadHandler;
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
mBtn.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
initThread();
}
});
}
private void initView() {
mTv = (TextView) findViewById(R.id.tv);
mBtn = (Button) findViewById(R.id.btn);
}
private void initThread() {
//創(chuàng)建一個HandlerThread并開啟線程
mHandlerThread = new HandlerThread("update-msg");
mHandlerThread.start();
//從mHandlerThread中得到Looper并創(chuàng)建Handler
mThreadHandler = new Handler(mHandlerThread.getLooper()) {
@Override public void handleMessage(Message msg) {
Log.v(TAG, "currentThread===>" + Thread.currentThread() + " what====>" + msg.what);
try {
update();
} catch (Exception e) {
e.printStackTrace();
}
mThreadHandler.sendEmptyMessage(200);
}
};
mThreadHandler.sendEmptyMessage(200);
}
private void update() throws Exception {
Thread.sleep(3000);
runOnUiThread(new Runnable() {
@Override public void run() {
String result = "每隔3s更新一次:";
result += Math.random();
mTv.setText(result);
}
});
}
}
輸出的日志如下:
currentThread===>Thread[update-msg,5,main] what====>200
從日志我們可以看出handleMessage運行在我們創(chuàng)建的HandlerThread("update-msg")之下。我們有理由懷疑這跟我們傳入的mHandlerThread.getLooper()有關胸蛛。我們的mThreadHandler 是在UI線程中創(chuàng)建的污茵,按理來說handleMessage應該運行在UI線程中才對。了解Handler原理的都知道葬项,handleMessage方法是在Handler的dispatchMessage方法中被調用的且dispatchMessage方法沒有進行線程切換泞当。所以線程切換應該發(fā)生在dispatchMessage被調用的地方,那dispatchMessage是在哪被調用的呢民珍?我們發(fā)現(xiàn)Loop的loop()方法中調用了dispatchMessage()方法襟士。(這里我就不貼Handler和Loop相關的代碼,感興趣的可以參考我以前的文章:Handler的原理)而且我們還發(fā)現(xiàn)了loop()方法的注釋如下:
意思是loop()方法運行在Loop被綁定的線程中嚷量。
那Loop又是在什么時候被綁定的呢陋桂?
就是這2個方法對Loop進行了綁定。那這個sThreadLocal又是什么鬼津肛?它到底有什么用章喉?別急,我們去看下它創(chuàng)建的地方:
它其實就是一個ThreadLocal身坐,關于ThreadLocal的原理秸脱,大家可以參考:ThreadLocal源碼深入分析
在這里我簡單的說下,其實ThreadLocal的作用部蛇,就是通過Thread中的threadLocals:ThreadLocalMap變量將我們通過ThreadLocal#set方法傳進來的數(shù)據(jù)跟Thread進行綁定摊唇,從而保證了訪問到的變量屬于當前線程,每個線程都保存有一個變量副本涯鲁,每個線程的變量都不同巷查,而同一個線程在任何時候訪問這個本地變量的結果都是一致的。當此線程結束生命周期時抹腿,所有的線程本地實例都會被GC岛请。ThreadLocal相當于提供了一種線程隔離,將變量與線程相綁定警绩。ThreadLocal通常定義為private static類型崇败。
通過上面的分析,我們已經(jīng)知道了Handler#handleMessage方法會運行在Loop說綁定的線程上肩祥,驗證了我們一開始的猜想后室。這里Loop是從我們創(chuàng)建的HandlerThread中得到的,而HandlerThread其實就是一個線程混狠,所以Loop綁定在了新創(chuàng)建的HandlerThread上岸霹。但是我們并不滿足于此,我們得進一步看看HandlerThread和普通的Thread到底有什么不一樣将饺。
HandlerThread的源碼解析
HandlerThread的代碼量其實并不多贡避,它繼承于Thread痛黎,主要的方法其實就是run方法
@Override
public void run() {
mTid = Process.myTid();
//創(chuàng)建Loop并綁定當前線程
Looper.prepare();
//關鍵代碼
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
//設置線程優(yōu)先級
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
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;
}
短短的幾行代碼確幾乎實現(xiàn)了我們想要的所有功能,我們來看關鍵代碼run方法中的synchronized 代碼塊其實對應于getLooper方法中的synchronized代碼塊贸桶,這樣做的目的是為了確保舅逸,我們通過getLoop()方法得到的Loop對象一定是被初始化后的Loop。當Loop被初始化以后會調用抽象方法onLooperPrepared()皇筛,他一般被用于在開啟隊列循環(huán)之前做一些初始化的操作,然后執(zhí)行任務隊列坠七。
總結
HandlerThread的原理已經(jīng)分析完了水醋,我們來總結一下它的特點:
1.HandlerThread它就是一個線程,和開啟普通的線程得到操作一致
2.HandlerThread需要搭配Handler使用彪置,單獨使用的意義不大
3.HandlerThread會將通過handleMessage傳遞進來的任務進行串行執(zhí)行拄踪,這是由messageQueue的特性決定的,從而也說明了HandlerThread效率相比并行操作會比較低
IntentService的使用和原理
分析完HandlerThread之后我們來分析一下IntentService的使用和原理拳魁,老規(guī)矩我們先看怎么使用惶桐。
IntentService的使用
public class MyIntentService extends IntentService {
private static final String TAG = "MyIntentService";
public MyIntentService() {
super("MyIntentService");
Log.v(TAG,
"MyIntentService===>MyIntentService()" + " currentThread==>" + Thread.currentThread());
}
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*
* @param name Used to name the worker thread, important only for debugging.
*/
public MyIntentService(String name) {
super(name);
Log.v(TAG,
"MyIntentService===>MyIntentService(name)" + " currentThread==>" + Thread.currentThread());
}
@Override public void onCreate() {
super.onCreate();
Log.v(TAG, "MyIntentService===>onCreate" + " currentThread==>" + Thread.currentThread());
}
@Override protected void onHandleIntent(@Nullable Intent intent) {
Log.v(TAG, "MyIntentService===>onHandleIntent" + " currentThread==>" + Thread.currentThread());
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
Log.v(TAG, "MyIntentService===>onStartCommand" + " currentThread==>" + Thread.currentThread());
return super.onStartCommand(intent, flags, startId);
}
}
調用服務和我們普通的Service一致
輸出的日志如下
MyIntentService===>MyIntentService() currentThread==>Thread[main,5,main]
MyIntentService===>onCreate currentThread==>Thread[main,5,main]
MyIntentService===>onStartCommand currentThread==>Thread[main,5,main]
MyIntentService===>onHandleIntent currentThread==>Thread[IntentService[MyIntentService],5,main]
從中我們可以看出onHandleIntent方法是運行在子線程中的,更有意思的是潘懊,當我們在onHandleIntent 方法中執(zhí)行延遲操作時姚糊,打印的日志如下描述:
1、當服務沒執(zhí)行完時又點擊了開啟服務的操作授舟,此時救恨,onStartCommand方法會立即執(zhí)行,而onHandleIntent方法會在上一個任務執(zhí)行完以后再去執(zhí)行onHandleIntent方法释树。
2肠槽、當服務已經(jīng)執(zhí)行完被自動結束以后,再去調用service奢啥,輸出的日志和第一次輸出的日志一致秸仙。
可能我說的比較抽象,大家自取去操作一遍就會發(fā)現(xiàn)我所說的有意思的地方桩盲。從上面的日志輸出寂纪,我們可以得出以下結論:
1、IntentService在任務執(zhí)行完以后會自動結束
2正驻、IntentService接收的任務是串行執(zhí)行的弊攘,并且互不干擾
3、IntentService的生命周期和普通的Service一致姑曙,只不過多了一個onHandleIntent回調方法襟交,并且它是串行回調的,等待上一個任務執(zhí)行完以后才會再次被調用
但是為什么會這樣呢伤靠?大家有沒有想過捣域。當然啼染,所有的答案都隱藏在源碼里,讓我們一起去揭開他神秘的面紗吧焕梅。
IntentService源碼解析
首先我們先來看下IntentService的幾個成員變量迹鹅,如下圖所示:
關于Loop和Handler我們都很熟悉了,前者是遍歷消息隊列的消息泵后者則是處理Handler發(fā)送過來的消息的贞言。下面我來看下他們初始化得到地方斜棚。
Loop初始化
原來他們都是在Service的onCreate回調方法中被初始化的。
通過上文HandlerThread的分析该窗,我們知道ServiceHandler的handleMessage方法會運行在mServiceLooper綁定的指定線程上弟蚀。這里這也就驗證了我們上文日志的輸出。
下面我們來解決另外一個問題酗失,也就是IntentService的生命周期函數(shù)的執(zhí)行情況义钉。
請看下面的代碼:
我們都知道當服務被啟動以后,再次調用服務的時候都會回調onStartCommand方法规肴,onStartCommand又調用了onStart方法捶闸,而onStart方法中只是通過Handler發(fā)送一個異步消息,然后ServiceHandler的handleMessage收到消息以后調用了onHandleIntent拖刃,這也就驗證了上文的日志輸出删壮。
下面我們來重點分析一下Service的stopSelf()方法,他有兩個重載方法序调,一個有參醉锅,一個無參,那他們之間有什么不同呢发绢?
我們還是通過源碼來看一下吧硬耍。
可以看到無參方法只是簡單的調用了有參方法,并傳入了一個-1的參數(shù)边酒。所以我們只有直接分析有參的方法就可以经柴。
由于Android sdk并沒有開放ActivityManageProxy(我們知道ActivityManage在客戶端得到代理是ActivityManageProxy)的代碼,所以我們只能通過查找相關資料來解決我們的疑惑墩朦。
最終我在官網(wǎng)上得到的答案如下:
簡單來說就是stopSelf中的startId對應于onStartCommand中的startId坯认,當stopSelf(startId)中的startId等于onStartCommand中的最后一個進來的startId的時候,就代表消息隊列中沒有更多的消息需要處理了氓涣,所以執(zhí)行完當前的消息以后牛哺,會去執(zhí)行Service的stop操作。
總結
關于IntentService的分析到這就告一段落了劳吠,其實IntentService就是基于HandlerThread機制來實現(xiàn)的引润,它允許我們在onHandleIntent回調方法中執(zhí)行異步操作。同時要注意他的生命周期回調函數(shù)的差異痒玩。下面貼上官網(wǎng)上關于IntentService類的介紹淳附,幫助大家理解议慰。