前言
在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的请求。
参考
- Android 7.1.1 (API level 25)
- https://developer.android.com/reference/android/app/IntentService.html
- https://developer.android.com/guide/components/services.html