前言
提到Android的多線程機制,除了我們常用的Thread來實現異步任務之外,還有
- AsyncTask:封裝了線程池和Handler,主要爲了子線程更新UI;
- HandlerThread:一個已經擁有了Looper的線程類,內部可以直接使用Handler;
- IntentService:一個內部採用HandlerThread來執行任務的Service服務,任務執行完畢後會自動退出;
今天我們來根據平時的使用方式來分析一下第三個IntentSevice到底是什麼怎麼實現的?
使用IntentService
默認的 Service 是執行在主線程的,可是通常情況下,這很容易影響到程序的繪製性能(搶佔了主線程的資源)。除了 AsyncTask 與 HandlerThread,我們還可以選擇使用 IntentService 來實現異步操作。IntentService 繼承自普通 Service 同時又在內部創建了一個 HandlerThread,在 onHandlerIntent()
的回調裏面處理扔到 IntentService 的任務。所以 IntentService 就不僅僅具備了異步線程的特性,還同時保留了 Service 不受主頁面生命週期影響的特點。
IntentService它本身是一個抽象類,因此使用它必須創建它的子類才能使用。所以這裏我們自定義一個MyIntentService,來處理異步任務:
public class MyIntentService extends IntentService {
private static final String TAG = "MyIntentService";
private boolean isRunning = true;
private int count = 0;
public MyIntentService() {
super("MyIntentService");
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
protected void onHandleIntent(Intent intent) {
try {
//查看線程id
Log.i(TAG, intent.getStringExtra("params") + ", 線程id:" + Thread.currentThread().getId());
Thread.sleep(1000);
//從0-100漸增
isRunning = true;
count = 0;
while (isRunning) {
count++;
Log.i(TAG, "MyIntentService 線程運行中..." + count);
if (count >= 100) {
isRunning = false;
}
Thread.sleep(50);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void onDestroy() {
super.onDestroy();
}
}
然後啓動服務之前別忘了在manifest文件中註冊這個Service:
// 在 Manifest 中註冊服務
<service android:name=".service.MyIntentService"/>
最後是啓動服務,就和普通Service一樣啓動:
// 像啓動 Service 那樣啓動 IntentService
Intent intent= new Intent(getActivity(), MyIntentService.class);
intent.putExtra("params", "testString...");
getActivity().startService(intent);
到此,通過IntentService執行的異步任務已經開始執行了,當執行完畢之後它會自動停止而不用我們手動操作。
當這個MyIntentService啓動之後,我們看到它接收到了消息並打印出了傳遞過去的intent參數,同時顯示onHandlerIntent方法執行的線程ID並非主線程,也就是說它果真開了一個額外的線程,什麼時候開啓的呢?我們進入IntentService源碼看看。
IntentService源碼
//IntentService繼承了Service並且它本身是一個抽象類,因此使用它必須創建它的子類才能使用。
public abstract class IntentService extends Service {
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
private String mName;
private boolean mRedelivery;
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
// onHandleIntent 方法在工作線程中執行,執行完調用 stopSelf() 結束服務。
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
}
public IntentService(String name) {
super();
mName = name;
}
public void setIntentRedelivery(boolean enabled) {
mRedelivery = enabled;
}
@Override
public void onCreate() {
// TODO: It would be nice to have an option to hold a partial wakelock
// during processing, and to have a static startService(Context, Intent)
// method that would launch the service & hand off a wakelock.
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
@Override
public void onDestroy() {
mServiceLooper.quit();
}
@Override
@Nullable
public IBinder onBind(Intent intent) {
return null;
}
@WorkerThread
protected abstract void onHandleIntent(@Nullable Intent intent);
}
代碼還是相當的簡潔的,首先通過定義我們可以知道IntentService是一個Service,並且是一個抽象類,所以我們在繼承IntentService的時候需要實現其抽象方法:onHandlerIntent。
1. 啓動 IntentService 爲什麼不需要新建線程?
我們來看看它的onCreate()函數:
private volatile ServiceHandler mServiceHandler;
@Override
public void onCreate() {
super.onCreate();
// HandlerThread 繼承自 Thread,內部封裝了 Looper,在這裏新建線程並啓動,所以啓動 IntentService 不需要新建線程。
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
// 獲得工作線程的 Looper,並維護自己的消息隊列MessageQueue
mServiceLooper = thread.getLooper();
// mServiceHandler 是屬於這個工作線程的
mServiceHandler = new ServiceHandler(mServiceLooper);
}
我們可以發現其內部定義一個HandlerThread(本質上是一個含有消息隊列的線程)。然後用成員變量維護其Looper和Handler,由於其Handler(也就是mServiceHandler對象)關聯着這個HandlerThread的Looper對象,所以這個ServiceHandler
的handleMessage方法在HandlerThread線程中執行。
然後我們發現其onStartCommand方法就是調用的其onStart方法,具體看一下其onStart方法:
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
很簡單就是將startId和啓動時接受到的intent對象傳遞到ServiceHandler的消息隊列中處理,那麼我們具體看一下ServiceHandler的處理邏輯:
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
// onHandleIntent 方法在工作線程中執行,執行完調用 stopSelf() 結束服務。
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
}
可以看到起handleMessage方法內部執行了兩個邏輯:
一個是調用了其
onHandlerIntent()
抽象方法,在子線程中執行。二是調用了
stopSelf()
方法,這裏需要注意的是stopSelf方法傳遞了msg.arg1
參數,從剛剛的onStart方法我們可以知道我們傳遞了startId
,這是由於service可以啓動多次,可以傳遞N次消息,當IntentService的消息隊列中含有消息時調用stopSelf(startId)並不會立即stop自己,只有當消息隊列中最後一個消息被執行完成時纔會真正的stop自身。
2. 爲什麼不建議通過 bindService() 啓動 IntentService?
我們看IntentService的onBind()
方法:
@Override
public IBinder onBind(Intent intent) {
return null;
}
IntentService 源碼中的 onBind()
默認返回 null,不適合 bindService()
啓動服務,如果你執意要 bindService()
來啓動 IntentService,可能因爲你想通過 Binder 或 Messenger 使得 IntentService 和 Activity 可以通信,這樣 onHandleIntent() 就不會被回調,相當於在你使用 Service 而不是 IntentService。
總結
IntentService 是繼承自 Service 並處理異步請求的一個抽象類,在 IntentService 內有一個工作線程來處理耗時操作,當任務執行完後,IntentService 會自動停止,不需要我們去手動結束。如果啓動 IntentService 多次,那麼每一個耗時操作會以工作隊列的方式在 IntentService 的 onHandleIntent 回調方法中執行,依次去執行,執行完自動結束。
IntentService有以下特點:
1). 它創建了一個獨立的工作線程來處理所有的通過onStartCommand()傳遞給服務的intents。
2). 創建了一個工作隊列,來逐個發送intent給onHandleIntent()。
3). 不需要主動調用stopSelft()來結束服務。因爲,在所有的intent被處理完後,系統會自動關閉服務。
4). 默認實現的onBind()返回null
5). 默認實現的onStartCommand()的目的是將intent插入到工作隊列中
繼承IntentService的類至少要實現兩個函數:構造函數和onHandleIntent()函數。要覆蓋IntentService的其它函數時,注意要通過super調用父類的對應的函數。
不過,使用 IntentService 需要特別留意以下幾點:
首先,因爲 IntentService 內置的是 HandlerThread 作爲異步線程,所以每一個交給 IntentService 的任務都將以隊列的方式逐個被執行到,一旦隊列中有某個任務執行時間過長,那麼就會導致後續的任務都會被延遲處理。
其次,通常使用到 IntentService 的時候,我們會結合使用 BroadcastReceiver 把工作線程的任務執行結果返回給主 UI 線程。使用廣播容易引起性能問題,我們可以使用 LocalBroadcastManager 來發送只在程序內部傳遞的廣播,從而提升廣播的性能。我們也可以使用 runOnUiThread() 快速回調到主 UI 線程。
最後,包含正在運行的 IntentService 的程序相比起純粹的後臺程序更不容易被系統殺死,該程序的優先級是介於前臺程序與純後臺程序之間的。