美文网首页
你是否真正理解了service的start和bind方式下的生命

你是否真正理解了service的start和bind方式下的生命

作者: Kip_Salens | 来源:发表于2018-02-11 18:12 被阅读0次

    对于四大组件之一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来实现的消息遍历,顺序执行,所以你开启服务的先后是有顺序的,先到先得哦!

    好了,就这些,没想到的情况可以补充哦!

    练习代码地址

    相关文章

      网友评论

          本文标题:你是否真正理解了service的start和bind方式下的生命

          本文链接:https://www.haomeiwen.com/subject/nnhltftx.html