对于四大组件之一Service,我们知道它是一般用于后台运行的,有两种方式开启Service,即Start方式和bind方式。
小编我在最近的面试中,就被面试官给考住了,把我给绕晕了,原因就是没有真正亲自验证一下这两种方式下Service的生命周期,这不,亡羊补牢来啦,走起!
1. Service是什么?
Service是android中实现后台运行的解决方案,既然在后台运行,一般不需要和用户进行交互,完成一些后台比较耗时的逻辑处理。当然,千万不要被迷惑,郭神在第一行代码中也说了,虽说叫后台服务,但是实际上也是运行在主线程中,那么你可有一些UI处理的话,也是可以的,比如弹出个Toast!但是更要注意的是,不要直接在这个服务中直接写比较耗时的操作,比如网络下载,文件读写,如果你不信,出现ANR你就信了。
有人就会问了,既然是服务,那为啥不是直接让开发者能够使用,完成一些耗时操作呢,还要自己进行多线程编程,嘿嘿,我暂时也不知道,谷歌为什么这么设计?
提到了多线程编程,那么在Service中怎么实现网络下载等耗时操作呢?这里只简单提一下,可以自己开线程,也可以使用AsycTask,还可以用IntentService,暂时只想到这些。
好了,有点扯远了,回归本题,只要来说说Service两种使用方式的生命周期。
2. Service启动方式
Start方式:
特点:
(1)一般用于Activity中不需要和Service交互,这也是和bind方式的主要区别
(2)通过startService方法开启后就和Activity的关系不大了,Activity的生命周期不再影响Service了,但是Activity有权关掉Service,通过调用stopService来暂停服务,或者在Service中自己调用StopSelf()来停止服务
(3)这种方式下的生命周期:
onCreate-------------第一次开启时调用
onStartCommand-------每次启动都会调用
onDestroy------------服务停止时调用
(4)onStartCommand方法返回值是int类型,有以下四种类型:
START_STICKY_COMPATIBILITY:START_STICKY的兼容版本,Service被kill后不保证onStartCommand会再执行
START_STICKY:Service开启后如果被kill后,会保留开启状态,但是不保存intent。系统会重启serveice,重新回调onStartCommand方法,如果没有Start命令,会传递一个为null的intent,所以需要对intent作非空判断。
START_NOT_STICKY:Service被kill后,不会保留开启状态,如果没有其他组件来开启,系统不会重启该Service。
START_REDELIVER_INTENT:从名字上可以看出,Service被kill掉之后,系统会重新传递上一次传过来intent,重启Service;该intent会保留到调用stopself。该参数下不传递值为null的intent。
(5)默认的返回值
依赖于目标SDK版本,看下源码
private boolean mStartCompatibility = false;//初始为false
public final void attach(
...
mStartCompatibility = getApplicationInfo().targetSdkVersion
< Build.VERSION_CODES.ECLAIR;//目前SDK版本小5是为true,大于等于5时为false
}
//从父类方法中看出,为false(SDK版本>=5)时,返回值为START_STICKY;
//为true(SDK版本<5)时,返回值为START_STICKY_COMPATIBILITY
public @StartResult int onStartCommand(Intent intent, @StartArgFlags int flags, int startId) {
onStart(intent, startId);
return mStartCompatibility ? START_STICKY_COMPATIBILITY : START_STICKY;
}
现在一般目标SDK都会大于5,所以默认返回值为START_STICKY,服务被kill掉后,系统会重新启动服务
bind方式:
特点:
一、一般用于和组件间进行通讯,如Activity要和Service之间通讯,则需要使用bind方式,在Service中需要创建一个binder,然后通过onBind方法返回给Activity,在Activity中创建一个ServiceConnection用于接收这个Binder,以上就是Service绑定方式的主要使用步骤。
二、bind方式开启的Service,与组件的生命周期相关,如,当Activity销毁时,Service也就结束了。但是,Activity销毁后,需要手动在onDestroy中unbind解绑Service,否则会出现内存泄漏,所以如果是绑定方式开启的服务,一定要记得解绑定。
MainActivity has leaked ServiceConnection com.ralf.www.servicetest.MainActivity$1@861526a that was originally bound here
android.app.ServiceConnectionLeaked: Activity com.ralf.www.servicetest.MainActivity has leaked ServiceConnection com.ralf.www.servicetest.MainActivity$1@861526a that was originally bound here
at android.app.LoadedApk$ServiceDispatcher.<init>(LoadedApk.java:1092)
at android.app.LoadedApk.getServiceDispatcher(LoadedApk.java:986)
at android.app.ContextImpl.bindServiceCommon(ContextImpl.java:1303)
at android.app.ContextImpl.bindService(ContextImpl.java:1286)
三、bind方式下的生命周期:假设一个Activity A,一个MyService
(1)A绑定后MyService后,会走onCreate----onBind,如果绑定再次绑定不会有任何操作,因为已经绑定了;如果A解绑定,会走MyService的onDestroy,
(2)A绑定MyService后,又调用start方式开启
onCreate----onBind----onStartCommand
此时如果解绑定,服务仍存在,若要停止服务,需要解绑定一次,stopService一次,没有先后顺序
3.与Activity的关系
首先,来回顾一下Activity的生命周期(这里测试使用的是nexus 5X,有虚拟按键),两个Activity A 和 B和一个MyService,A上有按钮可以开启B
(1)打开A,onCreate---onStart---onResume
(2)按back键,退出A到桌面:onPause----onStop---onDestroy
(3)接着如果重新打开A,onRestart----onStart---onResume
(4)如果按home(虚拟键的圆圈)键或者任务管理(虚拟键的方块),onPause---onStop
(5)息屏:onPause----onStop
(6)亮屏解锁:onRestart----onStart---onResume
(7)跳转活动B:onPause----onCreate(B)---onStart(B)---onResume(B)---onStop---onDestroy
(8)back返回A:onPause(B)----onCreate(A)---onStart(A)---onResume(A)---onStop(B)---onDestroy(B)
好,下面来看下在这两个活动下Service的生命周期
(1)A绑定Service,然后跳转B,B在绑定Service:onCreate----onBind---onDestroy---onCreate----onBind
(2)A绑定Service,然后跳转B,B start Service:onCreate----onBind---onDestroy---onCreate----onStarCommand
(3)A start Service,然后跳转B,B bind Service:onCreate----onStarCommand---onBind(B)
(4)A start Service,然后跳转B,B bind Service,back返回A,A bind Service(不会再走onBind):onCreate----onStarCommand---onBind(B)
(5)A start Service,然后跳转B,B start Service:onCreate(A)----onStarCommand(A)---onStarCommand(B)
这里给出几种情况,并没有给出分析。但是总结一下,start后,Service一直存在,如果想要停止,可以在任意一个组件中调用一次StopService即可。start前提下开启的Service,初次bind会调用onBind,之后只要Service仍存在,后面再被bind,不会再走onBind。
4. IntentService
前面提到要想在Service中实现多线程编程,那么可以自己开启线程,但是有时可能忘记线程开启,而且Service也要自己停止。所以出现了一个IntentService,帮我们封装好了线程,实际上是HandlerThread和Handler的封装,这里具体分析里面的源码了,主要说一下IntentService的使用方法。
使用IntentService比较简单,只需要实现里面的onHandleIntent抽象方法即可,在里面可以加上一些耗时的操作,执行结束后,会自动调用stopself方法来结束服务。
可以自己验证,重写onDestroy方法,在里面打上Log,开启后看是否回调该方法。另外,可以验证下该线程的和主线程是否是一个线程。
不过有两点需要记住:
(1)就是onHandleIntent方法中的intent参数就是我们开启服务是传递的intent,通过这个intent也是传递一些参数给服务。
(2)该服务底层是通过looper来实现的消息遍历,顺序执行,所以你开启服务的先后是有顺序的,先到先得哦!
好了,就这些,没想到的情况可以补充哦!