IntentService继承于Service,它的特点是什么呢?就跟龙叔小程序的特点一样——“用完即走”。
用过IntentService的人都知道,使用非常简单,根本不用自己去建立线程啊,维护线程通讯啊,甚至连最后资源的释放也不用我们自己处理,我们只需专心于业务实现即可,也就是编写onHandleIntent(Intent intent)方法中的代码,具体使用这里就不多说了,其实我们在之前的文章:定时任务之Alarm,已经使用了IntentService,今天我们主要来分析一下IntentService原理。
分析原理,当然是要看源码的,我们一步步来看:
@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);
}
启动一个IntentService,当然先进onCreate方法啦,从上面源码我们可以看到,第一步我们创建了一个HandlerThread(至于HandlerThread相关的说明,可查看:Handler相关看这篇就够了),然后启动线程,获取Looper,然后将HandlerThread中的Looper与ServiceHandler进行绑定,好啦,我们看看ServiceHandler的源码:
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);
}
}
ServiceHandler是一个内部类,继承于Handler,在handleMessage中我们看到了熟悉的方法:onHandleIntent(Intent intent),哦,原来我们使用IntentService中最重要的覆盖的onHandleIntent方法是在这里调用的,这里我们说明几点:
1)因为ServiceHandler是与HandlerThread中的Looper绑定的,所以onHandleIntent方法是在子线程中执行的。
2)我们注意到执行完onHandleIntent后,调用了stopSelf(msg.arg1),这就是为什么IntentService能做到“用完即走”的原因了,因为执行完任务后它会停掉自己的服务。
3)注意:这里自己停掉服务用的是stopSelf(int startId)方法,而不是stopSelf() ,为什么呢?那么我们就得先说一下这两个API之间的区别了,首先我们看stopSelf() 的源码:
public final void stopSelf() {
stopSelf(-1);
}
发现原来stopSelf()调用的是stopSelf(int startId),只不过startId为-1而已。
说到这个startId,它是什么呢?其实它就是service的一个生命周期:onStartCommand(@Nullable Intent intent, int flags, int startId)中最后的一个参数。
我们都知道,当我们多次调用startService来启动同一个service时,只有第一次会执行onCreate,然后会多次调用onStartCommand,如果你去打印log的话,你会发现尽管onCreate只执行一次,但是每次的startId却是不同的,且都大于0。
而stopSelf(int startId)中的startId与onStartCommand中的startId是一一对应的关系,所以,当我们调用stopSelf(int startId)时,系统会检测是否还有其它的startId存在,有的话就不销毁当前service,没有的话则销毁。
而如果我们调用的是stopSelf(),那么无论是否还存在其它的startId,都会立即销毁当前service。
这就是stopSelf()和stopSelf(int startId)两个方法的区别!
我们回到之前的的问题,为什么IntentService中自停服务用的是stopSelf(int startId)而不是stopSelf()呢?
从上面比较两个方法的区别我们不能得出:这是为了提高IntentService的利用率,也就是说,如果在onHandleIntent方法执行完毕前,又调用了startService启动了同一个IntentService,那么我们就没必要销毁当前service了,直接继续用当前service对象执行任务即可,这样有利于减少了对象的销毁及创建。
另外:这里插一点,因为IntentService中用的是一个HandlerThread,也就是单一的线程,所以,用IntentService来执行任务只能是串行依次进行。
下面,我们继续分析源码,我们刚刚说到了onStartCommand,那我们也来看看它的源码:
@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;
}
onStartCommand第一步调用的是onStart,从onStart方法我们可以看出,其实就是用上面的handler发送了消息,从主线程切换到了子线程执行任务,这个没什么好说的。
然后我们看下面一行代码,返回值: mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
查看了一下mRedelivery默认值是false,当然提供了相应的方法可进行设置,下面我们先简单说明下onStartCommand几种返回值及区别:
1)START_STICKY:如果service进程被kill掉,保留service的状态为开始状态,但不保留递送的intent对象。随后系统会尝试重新创建service,由于服务状态为开始状态,所以创建服务后一定会调用onStartCommand(Intent,int,int)方法。如果在此期间没有任何启动命令被传递到service,那么参数Intent将为null。
2)START_NOT_STICKY:“非粘性的”。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统不会自动重启该服务
3)START_REDELIVER_INTENT:重传Intent。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统会自动重启该服务,并将Intent的值传入。
4)START_STICKY_COMPATIBILITY:START_STICKY的兼容版本,但不保证服务被kill后一定能重启。
从上面的介绍我们知道了,onStartCommand默认返回了START_NOT_STICKY,所以,这里注意了,如果你使用IntentService执行的任务非常重要的话,建议通过设置setIntentRedelivery将mRedelivery设置为true,这样一来onStartCommand的返回就变成了START_REDELIVER_INTENT,有利于异常情况下服务的重启及恢复。
最后,我来看看销毁的方法:
@Override
public void onDestroy() {
mServiceLooper.quit();
}
前面我们在Handler相关看这篇就够了这篇文章中介绍HandlerThread就说了,使用HandlerThread必须注意用完释放Looper,这不?IntentService在onDestroy生命周期中,帮我们进行了Looper的释放,所以我们得以“用完即走”,什么都不用处理,超级方便。
好啦,大概也就这些了,别看IntentService的源码也就一百多行,其实细究起来还是能学到不少东西的。