IntentService
IntentService
1、IntentService是什么?
回顾Android实现线程异步的方法,有AsyncTask、HandlerThread、线程池、IntentService。其中IntentService其实是继承与Service的类,内部是由HandlerThread实现(HandlerThread本系列由专门的小节做介绍,不清楚的童鞋可以去看看)。常用于处理异步请求,处理完子线程的耗时操作后,会自动执行stopService()。
2、IntentService的使用
我们用IntentService来实现在后台读取文件已经下载文件,直接上demo:
// XuruiIntentService.java
public class XuruiIntentService extends IntentService {
private static final String TAG = "XuruiIntentService";
public XuruiIntentService() {
//IntentService 工作线程的名字
super("XuruiIntentService");
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
if (intent != null) {
String taskName = intent.getStringExtra("taskName");
switch (taskName) {
case "readFile": //1
//任务一:执行读取大文件内容的耗时操作
try {
readFile();
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
break;
case "DownLoad": //2
//任务二:执行从网络下载文件的耗时操作
break;
default:
break;
}
}
}
}
// Activity中使用XuruiIntentService类
public void onClick(View v) {
Intent intent = new Intent(this, XuruiIntentService.class);
switch (v.getId()) {
case R.id.readFileBtn: //3
intent.putExtra("taskName", "readFile");
startService(intent);
break;
case R.id.downLoadBtn: //4
intent.putExtra("taskName", "DownLoad");
startService(intent);
break;
default:
break;
}
}
上面的demo还是比较好理解的,在注释3按下读取文件的按钮后,就会通过startService()启动服务,跑到注释1去执行耗时的文件读取操作。没错,IntentService的启动和Service的启动是一样的。
还有一点需要注意,如果按下readFileBtn后马上按downLoadBtn,那么代码执行的顺序是: onCreate、onStartCommand、onHandleIntent: readFile、onStartCommand、onHandleIntent: DownLoad、onDestroy
但如果按下readFileBtn后稍等一会再按downLoadBtn,那么代码执行的顺序是: onCreate、onStartCommand、onHandleIntent: task1、readFile、 onCreate、onStartCommand、onHandleIntent: task2、DownLoad
以上两种情况证明了,IntentService在处理完操作后,会自动退出,因此按下readFileBtn后稍等一会再按downLoadBtn,会重新拉起一个新的服务,从onCreate()开始执行。
注:不能用bindService()来绑定IntentService,因为onBind()默认返回null。
3、源码分析
因为内部使用HandlerThread实现,源码也才100+行,因此直接copy完整的源码,不做任何删减(下面源码是android9.0.0的源码)。
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((Intent)msg.obj); //1
stopSelf(msg.arg1); //2
}
}
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*
* @param name Used to name the worker thread, important only for debugging.
*/
public IntentService(String name) {
super();
mName = name;
}
/**
* Sets intent redelivery preferences. Usually called from the constructor
* with your preferred semantics.
*
* <p>If enabled is true,
* {@link #onStartCommand(Intent, int, int)} will return
* {@link Service#START_REDELIVER_INTENT}, so if this process dies before
* {@link #onHandleIntent(Intent)} returns, the process will be restarted
* and the intent redelivered. If multiple Intents have been sent, only
* the most recent one is guaranteed to be redelivered.
*
* <p>If enabled is false (the default),
* {@link #onStartCommand(Intent, int, int)} will return
* {@link Service#START_NOT_STICKY}, and if the process dies, the Intent
* dies along with it.
*/
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 + "]"); //3
thread.start(); //4
mServiceLooper = thread.getLooper(); //5.1
mServiceHandler = new ServiceHandler(mServiceLooper); //5.2
}
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg); //6
}
/**
* You should not override this method for your IntentService. Instead,
* override {@link #onHandleIntent}, which the system calls when the IntentService
* receives a start request.
* @see android.app.Service#onStartCommand
*/
@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(); //7
}
/**
* Unless you provide binding for your service, you don't need to implement this
* method, because the default implementation returns null.
* @see android.app.Service#onBind
*/
@Override
@Nullable
public IBinder onBind(Intent intent) {
return null;
}
/**
* This method is invoked on the worker thread with a request to process.
* Only one Intent is processed at a time, but the processing happens on a
* worker thread that runs independently from other application logic.
* So, if this code takes a long time, it will hold up other requests to
* the same IntentService, but it will not hold up anything else.
* When all requests have been handled, the IntentService stops itself,
* so you should not call {@link #stopSelf}.
*
* @param intent The value passed to {@link
* android.content.Context#startService(Intent)}.
* This may be null if the service is being restarted after
* its process has gone away; see
* {@link android.app.Service#onStartCommand}
* for details.
*/
@WorkerThread
protected abstract void onHandleIntent(@Nullable Intent intent);
}
}
如果已经学习过本系列Service和HandlerThread两小节的同学,看上面的代码就非常容易了。我们以上一小节,按下readFileBtn来读取文件内容为例,看看IntentService源码都做了什么?
// 按下readFileBtn后执行:
intent.putExtra("taskName", "readFile");
startService(intent);
启动服务后,流程如下:
- 首先执行onCreate(),里面的注释3-5正是HandlerThread三大经典的使用步骤,对应[注释3-5];
- 接着执行onStartCommand() -> onStart(),通过子线程的mServiceHandler发送消息,对应[注释6];
- 发送信息就有接收信息,执行子线程handleMessage()中调用我们重写的 onHandlerIntent 执行异步任务,对应[注释1];
- 等待所有任务执行完后,调用[注释2] stopSelf(msg.arg1)来销毁服务,可以发现该方法带了参数,意为尝试停止服务之前会判断最近启动服务的次数是否是startId,如果相等才停止服务。
因为onCreate()只会执行一次,但onStartCommand()可以执行多次,所以多次调用startService(Intent) 时,上面2-3步骤可能发送和接收多个消息,通过复写onHandlerIntent(),再根据发送不同的Intent,进行不同的线程操作。且发送多个消息时,对应的多个任务需要按顺序逐个执行。
4、Service 与 IntentService 的区别
- Service不是单独的线程,如果在onStartCommand()中执行耗时操作可能发生ANR,IntentService会创建一个工作线程来处理多线程任务;
- Service长期存在后台,结束需要主动调用stopSelft()来结束服务,而IntentService会在任务完成后默认调用stopSelft()直接退出;
- 两者都可以是使用startService()启动,但IntentService不能onBind();
5、使用场景
IntentService适合多个任务需要按顺序,适用更高优先级的的后台任务,不容易被系统杀死的使用场景,比如离线下载场景,后台下载场景。