前言
Service作为Android的四大组件之一,实现了在后台执行不需要界面的任务,例如音乐的播放等,若要实现一个在后台执行的耗时任务呢?例如离线下载。于是,Android 实现了 IntentService ,通过开启一个子线程在后台执行耗时任务。
1. 使用步骤
1)新建IntentService子类MyIntentService
2)重写 onHandleIntent()
方法,根据传入的不同Intent处理不同的任务
3)在 AndroidManifest.xml
中注册服务MyIntentService
4)需要发起任务请求的地方,新建 Intent
对象,使用 Bundle
保存传入的数据
5)调用 Activity
的 startService(intent)
发起一次任务请求
MyIntentService.java
//step1: 新建IntentService子类MyIntentService
public class MyIntentService extends IntentService {
private String taskName;
//step2: 调用父类构造方法,传入线程名字
public MyIntentService() {
super("myIntentService");
}
@Override
public void onCreate() {
super.onCreate();
Log.i("myIntentService----", "onCreate");
}
//默认实现,将传入的Intent请求依次加入到消息队列中
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
Log.i("myIntentService----", "onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i("myIntentService----", "onDestroy");
}
}
AndroidManifest.xml
<service
android:name=".MyIntentService"
tools:ignore="ExportedService"/>
发起任务请求的方法:
//在需要发起任务请求的地方调用此方法
private void startIntentService() {
//step5: 新建Intent传入任务处理请求,指向MyIntentService
Intent task1 = new Intent(MainActivity.this, MyIntentService.class);
Bundle bundle1 = new Bundle();
bundle1.putString("taskName", "task1");
task1.putExtras(bundle1);
Intent task2 = new Intent(MainActivity.this, MyIntentService.class);
Bundle bundle2 = new Bundle();
bundle2.putString("taskName", "task2");
task2.putExtras(bundle2);
//step6: 启动服务,发起一次任务请求 (多次调用startService(intent) 则发起多次请求)
startService(task1);
startService(task2);
//再次发起task1的任务请求
startService(task1);
}
- bug记录
在AndoridManifest.xml文件中注册MyIntentService时,为Service添加了属性name,并在代码中用包名的方式调用报错:
Service Intent must be explicit: Intent
原因:Android 5.0(Lollipop) 之后规定不能以包名的方式定义Service的Intent,而应用显示声明new Intent(xxxActivity.this, xxxService.class);
2. 源码分析
1)IntentService的初始化
//step2: 调用父类构造方法,传入线程名字
public MyIntentService() {
super("myIntentService");
}
//调用了此构造方法,赋值线程名称mName
public IntentService(String name) {
super();
mName = name;
}
//IntentService的onCreate()中
@Override
public void onCreate() {
super.onCreate();
//下面即是开启一个HandlerThread线程的常用步骤,详细可参考之前文章中有关HandlerThread的解析
//创建一个新线程HandlerThread对象,传入线程名称mName
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
//启动线程
thread.start();
//获取HandlerThread自动创建的Looper
mServiceLooper = thread.getLooper();
//关联HandlerThread和IntentService的Handler对象
mServiceHandler = new ServiceHandler(mServiceLooper);
}
2)onHandleIntent(Intent intent)
//step3: 重写onHandleIntent,判断Intent类型,依次处理不同的任务
@Override
protected void onHandleIntent(Intent intent) {
...
}
//IntentService中,创建ServiceHandler类,继承自Hanlder
private final class ServiceHandler extends Handler {
//构造方法,传入ServiceHandler所在线程的Looper对象
public ServiceHandler(Looper looper) {
super(looper);
}
//复写handleMessage,处理任务请求
@Override
public void handleMessage(Message msg) {
//进入子类MyIntentService中重写的构造方法onHandleIntent(intent),这个方法的执行是在工作线程中
onHandleIntent((Intent)msg.obj);
//调用Service的stopSelf() 自动停止开启的IntentService服务,标记为startId
stopSelf(msg.arg1);
}
}
//子类必须实现的构造方法,工作线程执行,根据Intent
@WorkerThread
protected abstract void onHandleIntent(@Nullable Intent intent);
3)onStartCommand(intent, flags, startId)
//将传入的Intent请求依次加入到消息队列中
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
//IntentService中onStartCommand
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
//调用onStart(),传入发起的任务请求Intent和该次请求的id值startId
onStart(intent, startId);//——→跳转至onStart()方法
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
//onStart()方法
@Override
public void onStart(@Nullable Intent intent, int startId) {
//新建Message对象,存储intent和startId
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
//ServiceHandler发送这条消息Message到消息队列MessageQueue中,等待对应的Handler处理消息任务
mServiceHandler.sendMessage(msg);
}
4)onDestroy()
@Override
public void onDestroy() {
//IntentService结束即MessageQueue内部的Message处理完成时,
//自动退出当前线程所持有的Looper,Looper自动退出所持有的MessageQueue
mServiceLooper.quit();
}
5)onBind(intent)
@Override
@Nullable
public IBinder onBind(Intent intent) {
return null;
//返回为空
}
启动IntentService时,若采用bindService()方式启动,IntentService的生命周期为:
onCrtate() → onBind() → onUnBind() → onDestroy()
若采用startService()方式启动,IntentService的生命周期为:
onCrtate() → onStartCommand() → onDestroy()
从生命周期来看,bindService()不会执行onStartCommand(),而IntentService在onStartCommand()方法中实现消息的创建和发送入队操作,所以IntentService只能通过startService()的方式启动才能实现HandlerThread线程发送消息并在后台Service中执行耗时任务。
3. 应用场景
线程任务需要 按序
且在 后台执行
,所有任务都在同一个Thread的Looper中执行
离线下载
思考
-
为什么IntentService是
Service + HandlerThread + Handler
的结合?
1)IntentService 继承自 Service
;
2)在 IntentService 的 onCreate()
方法中,创建 HandlerThread
对象,开启子线程执行耗时任务;
3)创建内部类 ServiceHandler
继承自 Handler
,实现消息的发送和处理。
-
为什么IntentService会在执行完所有任务请求后自动停止Service?
内部类ServiceHandler的handleMessage(msg)方法中,调用了父类Service方法stopSelf(),停止了IntentService。
网友评论