Service(1)

你是否真正理解了service的start和bind

对于四大组件之一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,onCreate—-onStart—onResume

(4)如果按home(虚拟键的圆圈)键或者任务管理(虚拟键的方块),onPuse—onStop

(5)息屏:onPause—-onStop,如果时间长,内存不足可能被回收,然后走onDestroy

(6)亮屏解锁:onRestart—-onStart—onResume

(7)跳转活动B:onPause—-onCreate(B)—onStart(B)—onResume(B)—onStop

(8)back返回A:onPause(B)—-onRestart(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来实现的消息遍历,顺序执行,所以你开启服务的先后是有顺序的,先到先得哦!

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

练习代码地址:

打赏一个呗

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦