IntentService 是系統(tǒng)提供的一種 Service,它用 Intent 傳遞數(shù)據(jù)单默,用子線程執(zhí)行代碼。使用時需要繼承 IntentService,自定義 Intent 中攜帶的數(shù)據(jù)格式以及對應(yīng)的執(zhí)行代碼褂傀,外部調(diào)用時需要按照格式定義 Intent 數(shù)據(jù)并啟動服務(wù)。
什么時候用
Service 作為四大組件之一加勤,用來實現(xiàn)與 UI 無關(guān)的功能仙辟,但 Service 各個生命周期的方法仍然運行在 UI 線程中,這就需要將任務(wù)提交到子線程中執(zhí)行一些耗時任務(wù)鳄梅。IntentService 就是安卓系統(tǒng)提供的一種使用子線程執(zhí)行任務(wù)的 Service叠国。
IntentService 將任務(wù)提交到一個 worker 線程中,使用者只需要定義好 Intent 數(shù)據(jù)格式戴尸,實現(xiàn)解析 Intent 和執(zhí)行任務(wù)的代碼即可粟焊,使用起來非常簡單。同時 IntentService 保證多個任務(wù)在 worker 線程中按提交順序串行執(zhí)行孙蒙。這個 worker 線程不會一直存在项棠,一旦任務(wù)執(zhí)行完畢,IntentService 就會自行銷毀挎峦。
因此香追,IntentService
適合:
- 需要串行運行的任務(wù)
- 輕量級耗時任務(wù),不能在 UI 線程執(zhí)行坦胶,但多個任務(wù)順序執(zhí)行不會阻塞
不適合:
- 需要并行運行的任務(wù)透典,比如下載多個文件
- 需要一直運行的任務(wù),比如監(jiān)聽網(wǎng)絡(luò)請求
- 非常耗時的任務(wù)顿苇,會阻塞后面提交的任務(wù)
使用步驟
- 定義一個 Service 繼承自 IntentService峭咒。
public class MyIntentService extends IntentService {
}
- 定義數(shù)據(jù)格式,將其封裝在 Intent 中岖圈。
public static void startSave(Context context, String data, String name, int count) {
Intent intent = new Intent(context, MyIntentService.class);
intent.putExtra("data", data);
intent.putExtra("name", name);
intent.putExtra("count", count);
context.startService(intent);
}
- 實現(xiàn)
protected void onHandleIntent(Intent intent)
方法- 解析 Intent 中的參數(shù)
- 根據(jù)參數(shù)執(zhí)行任務(wù)
@Override
protected void onHandleIntent(@Nullable Intent intent) {
int function = intent.getIntExtra("function", 0);
switch (function) {
case FUNCTION_SAVE:
String data = intent.getStringExtra("data");
String name = intent.getStringExtra("name");
int count = intent.getIntExtra("count", 0);
doFunction(data, name, count);
break;
}
}
- 在需要執(zhí)行任務(wù)的地方讹语,啟動服務(wù)
MyIntentService.startSave(this, "569585687455236541789355", "image", 1);
源碼分析
HandlerThread
在分析 IntentService 的源碼之前,先簡單介紹一下 HandlerThread蜂科,因為這是 IntentService 的核心顽决。
從名字可以看出來 HandlerThread 是一個線程短条,一個帶 Handler 的線程,可以通過 Handler 發(fā)送消息與線程通信才菠。它的源碼也非常簡單茸时,去掉注釋不到一百行,下面簡單分析一下 run()
方法和 getLooper()
方法赋访。
下面是 run()
方法和 getLooper()
方法的簡化版源碼可都,刪除了一些不太重要的代碼。
public void run() {
Looper.prepare(); // Looper 使用第一步 prepare()
synchronized (this) { // 這里鎖一下蚓耽,保護 mLooper 變量渠牲。
mLooper = Looper.myLooper(); // 獲得 Looper 引用
notifyAll();
}
Looper.loop(); // Looper 使用最后一步,開始無限消息循環(huán)
}
public Looper getLooper() {
// 線程 alive 狀態(tài)才有意義步悠,所以必須先調(diào)用線程的 start()签杈,再調(diào)用這個方法。
if (!isAlive()) {
return null;
}
// 這里的 synchronized 鎖對應(yīng) run() 方法中的 synchronized 鎖鼎兽。
// run() 方法中對 mLooper 的初始化代碼運行在這個 HandlerThread 中答姥,而 getLooper() 方法讀取
// mLooper 變量運行在另一個線程中。沒錯谚咬,這就是一個簡單的生產(chǎn)者/消費者的場景鹦付。
synchronized (this) {
// 判斷 alive 考慮 while 循環(huán)運行過程中線程中止的情況。
while (isAlive() && mLooper == null) { // 循環(huán)套 wait() 择卦,下面詳細解釋敲长。
try {
wait(); // 等待 mLooper 初始化完成后的 notify
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
循環(huán)套 wait()
的寫法,是并發(fā)代碼的標準套路互捌。
-
wait()
是為了等待一個條件是否滿足潘明,這里是等待mLooper
可用行剂。但觸發(fā)wait()
繼續(xù)執(zhí)行的notify()
卻并不一定是因為準備好了mLooper
才執(zhí)行的秕噪。wait()
繼續(xù)執(zhí)行了不代表條件就滿足了,因此要循環(huán)重復判斷條件才能確保條件是真的滿足了厚宰。 - wait() 還可能拋出
InterruptedException
腌巾,例如被Thread.interrupt()
中斷,這時等待的條件不一定被滿足铲觉,需要重復檢查澈蝙。
可以看 Java 官方文檔有詳細的解釋,講得非常好:Guarded Blocks
IntentService.onCreat()
初始化 worker 線程撵幽,也就是 HandlerThread灯荧。可見 IntentService 的核心是通過 Handler 消息機制來與 worker 線程通信盐杂。這幾句代碼也是 HandlerThread 的一個典型使用步驟逗载。
@Override
public void onCreate() {
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper); // 自定義 Handler 處理消息
}
IntentService.onStart()
每次啟動服務(wù)就是一次任務(wù)提交哆窿,任務(wù)自定義的數(shù)據(jù)和參數(shù)都封裝在了 Intent 對象中,這個 Intent 對象被封裝到了 Message 對象中厉斟,傳遞給 worker 線程挚躯。
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
注意還有個 startId 也保存到了 Message 中,這是用來自動停止服務(wù)用的擦秽,可以參考上一篇文章有詳細說明 startId 如何用來停止 Service:Android - Service Start/Stop 使用
ServiceHandler
HandlerThread 的 Handler码荔,handleMessage()
方法在 worker 線程中執(zhí)行。調(diào)用 IntentService 子類的抽象方法 onHandleIntent() 來自定義業(yè)務(wù)邏輯感挥。
private final class ServiceHandler extends Handler {
@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
}
運行完成之后缩搅,執(zhí)行 stopSelf(startId)
結(jié)束本次任務(wù),注意是本次任務(wù)而不是整個服務(wù)触幼。參數(shù) startId 的意義就在于它代表了一次任務(wù)的提交誉己,使 stopSelf 方法不會直接停止服務(wù),而是停止一個任務(wù)域蜗。內(nèi)部實現(xiàn)是用最后一個 startId 來判斷的巨双,只有這個 startId 是最后一個通過 onStartCommand 傳遞來的 startId,stopSelf(startId) 才會停止任務(wù)霉祸,否則忽略筑累。
IntentService 自動停止和串行的原理
- 因為 stopSelf(startId) 保證所有提交的任務(wù)處理完成后才停止服務(wù),使得 IntentService 在提交任務(wù)速度大于任務(wù)處理速度的時候丝蹭,能保持服務(wù)持續(xù)運行慢宗,不會重復創(chuàng)建多個服務(wù)浪費資源。
- 得益于 Handler 消息可以串行處理奔穿,IntentService 處理任務(wù)是串行執(zhí)行在 worker 線程中的镜沽。