前言
在Android中,Service是運行在主線程之中的。如果要在Service中執行耗時的任務,那麼我們需要手動創建子線程。並且,在任務完成的時候,我們需要手動停止Service。爲了簡化這些操作,Android提供了一個IntentService類。IntentService是Service的子類,它使用了一個HandlerThread線程來依次處理每個啓動Service的請求,並在所有的請求都處理完畢之後自動停止Service。
基礎知識
IntentService是一個Service,我們需要熟悉Service的生命週期,具體可以閱讀 Service的生命週期 這篇文章。
IntentService內部使用了一個HandlerThread線程,我們需要熟悉HandlerThread的工作原理,具體可以閱讀 HandlerThread源碼分析 這篇文章。
基本用法
首先,我們需要寫一個繼承自IntentService的類,並實現 onHandleIntent() 抽象方法。IntentService沒有默認的構造方法,所以我們還需要聲明一個默認的構造方法。示例代碼如下所示:
public class MyIntentService extends IntentService {
public MyIntentService() {
super("MyIntentService");
}
@Override
protected void onHandleIntent(Intent intent) {
// execute your task
}
}
其中,構造方法調用了super()方法,它用來命名IntentService內部的HandlerThread線程名。onHandleIntent()方法是在HanderThread線程中執行的,我們可以直接在該方法中執行耗時的任務。
然後,我們就可以在其它的組件(比如,Activity和BroadcastReceiver)中調用startService()方法來啓動該IntentService了。示例代碼如下所示:
Intent intent = new Intent(this, MyIntentService.class);
startService(intent);
最後,別忘了要在AndroidManifest.xml文件中註冊該IntentService類。示例代碼如下所示:
<application
...
<service
android:name=".MyIntentService"
android:exported="false" />
</application>
源碼分析
IntentService是一個繼承自Service的抽象類,它聲明瞭一個onHandleIntent()抽象方法。
public abstract class IntentService extends Service {
...
@WorkerThread
protected abstract void onHandleIntent(@Nullable Intent intent);
}
首先,我們來看IntentService的構造方法。
public IntentService(String name) {
super();
mName = name;
}
構造方法必須被子類的構造方法所調用。它保存了一個字符串在mName成員變量中,用來命名IntentService內部的HandlerThread線程名。
當我們在其它的組件(比如,Activity和BroadcastReceiver)中調用startService()方法來啓動Service時,如果Service還沒有創建,那麼系統將創建Service並回調Service的onCreate()方法。所以,接下來我們來看IntentService的onCreate()方法。
@Override
public void onCreate() {
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
在onCreate()方法中,IntentService創建了一個HanderThread線程,並啓動了該線程。接着,它獲取了該線程的Looper,並使用該Looper創建了一個與該線程相關聯的Handler。這樣,IntentService就可以通過該Handler來控制HandlerThread線程執行耗時的任務。
回調onCreate()方法之後,系統將回調Service的onStartCommand()方法。所以,接下來我們來看IntentService的onStartCommand()方法。
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
onStartCommand()方法調用了onStart()方法。
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
在onStart()方法中,IntentService使用了與HandlerThread線程相關聯的Handler發送了一條Message。因此,其它的組件每次調用startService()方法來啓動Service時,IntentService都會發送一條Message給HandlerThread線程。
HandlerThread線程的Looper從MessageQueue中循環地取出Message進行處理。最終Message是在與HandlerThread線程相關聯的Handler中進行處理的。所以,接下來我們來看與HandlerThread線程相關聯的Handler是如何處理Message的。
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
}
在handleMessage()方法中,onHandleIntent()方法被調用。因爲該Handler是與HandlerThread線程相關聯的,所以onHandleIntent()方法是在HandlerThread線程中執行的,我們可以直接在該方法中執行耗時的任務。通過Message中Intent,我們可以區分不同的啓動Service請求。因爲Looper是從MessageQueue中循環地取出Message進行處理的,所以IntentService是以隊列的方式來處理多個啓動Service的請求。
在handleMessage()方法的最後調用了Service的stopSelf()方法來自動停止Service。啓動Service的啓動ID被傳遞給stopSelf()方法,以確保處理完最後一次啓動Service的請求之後才停止Service。
在停止Service時,系統會回調Service的onDestroy()方法。所以,最後我們來看IntentService的onDestroy()方法。
@Override
public void onDestroy() {
mServiceLooper.quit();
}
在onDestroy()方法中,IntentService調用了與HandlerThread線程相關聯的Looper的quit()方法。quit()方法退出了HandlerThread線程的Looper消息循環,結束了HandlerThread線程。
總結
IntentService是Service的子類,它使用了一個HandlerThread線程來依次處理每個啓動Service的請求,並在所有的請求都處理完畢之後自動停止Service。
例子
接下來舉一個簡單的例子來實踐IntentService。項目源碼地址:https://github.com/chongyucaiyan/ServiceDemo
首先,新建一個繼承自IntentService的類。具體的代碼和註釋如下所示:
public class MyIntentService extends IntentService {
private static final String TAG = "MyIntentService";
// Action
public static final String ACTION_DO_TASK1 = "com.github.cyc.intentservice.MyIntentService.do_task1";
public static final String ACTION_DO_TASK2 = "com.github.cyc.intentservice.MyIntentService.do_task2";
public MyIntentService() {
// 指定HandlerThread線程名
super("MyIntentService");
}
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "onCreate(), current thread is " + Thread.currentThread().getName());
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "onStartCommand(), current thread is " + Thread.currentThread().getName());
return super.onStartCommand(intent, flags, startId);
}
@Override
protected void onHandleIntent(Intent intent) {
if (intent != null) {
Log.i(TAG, "onHandleIntent(), current thread is " + Thread.currentThread().getName());
if (ACTION_DO_TASK1.equals(intent.getAction())) {
// 執行任務1
doTask1();
} else if (ACTION_DO_TASK2.equals(intent.getAction())) {
// 執行任務2
doTask2();
}
}
}
private void doTask1() {
Log.i(TAG, "doTask1(), start");
// 模擬耗時任務
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.i(TAG, "doTask1(), end");
}
private void doTask2() {
Log.i(TAG, "doTask2(), start");
// 模擬耗時任務
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.i(TAG, "doTask2(), end");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG, "onDestroy(), current thread is " + Thread.currentThread().getName());
}
}
我們在onCreate()、onStartCommand()、onHandleIntent()和onDestroy()方法中打印了當前線程的名字。在onHandleIntent()方法中,我們根據Intent的Action來區分具體執行哪個耗時任務。
然後,我們在MainActivity頁面中放置了兩個按鈕。具體的代碼和註釋如下所示:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initContentView();
}
private void initContentView() {
findViewById(R.id.btn_main_start_service1).setOnClickListener(this);
findViewById(R.id.btn_main_start_service2).setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_main_start_service1:
// 啓動Service
Intent intent1 = new Intent(this, MyIntentService.class);
intent1.setAction(MyIntentService.ACTION_DO_TASK1);
startService(intent1);
break;
case R.id.btn_main_start_service2:
// 啓動Service
Intent intent2 = new Intent(this, MyIntentService.class);
intent2.setAction(MyIntentService.ACTION_DO_TASK2);
startService(intent2);
break;
default:
break;
}
}
}
當點擊Start Service1按鈕時,啓動Service執行任務1。當點擊Start Service2按鈕時,啓動Service執行任務2。
最後,別忘了要在AndroidManifest.xml文件中註冊該IntentService類。具體的代碼如下所示:
<application
...
<service
android:name=".MyIntentService"
android:exported="false" />
</application>
接下來,我們來運行一下。先點擊Start Service1按鈕,打印的信息如下圖所示:
可以看到,先是創建Service,然後在onHandleIntent()方法中執行任務,最後任務執行完畢自動停止Service。根據打印的線程信息,我們知道onCreate()、onStartCommand()、和onDestroy()方法都是在主線程之中執行的,而onHandleIntent()方法是在HandlerThread線程中執行的。
然後,快速地點擊Start Service1和Start Service2按鈕各一下,打印的信息如下圖所示:
可以看到,啓動Service、任務執行、停止Service的過程與上面類似。需要注意的是,任務2是在任務1執行完畢之後纔開始執行的,這驗證了IntentService是以隊列的方式來處理多個啓動Service的請求。