Service

1、启动模式

image

1.1 StartService() 模式

生命周期:onCreate()->onStartComment()->onDestroy()

生命周期见上图左时序图。当另一个组件可以通过调用 startService()方法来启动特定的服务,此时Service生命周期中的onStartCommand()方法被调用。当调用startService()方法时,其他组件需要在方法中传递一个intent参数,服务会在onStartCommand()中接收到intent并获取一些数据。比如ActivityA需要上报一些用户数据到服务器上面,就可以通过startService()来启动ServiceA,并将需要上报的数据传递给ServiceA,在ServiceA里面调用一些网络接口,把数据直接上报到服务器上面。

当一个服务以这种方式启动时,它的生命周期不再受启动它的组件的影响,它可以在后台无限期地运行,如上一段的例子里,就算ActvityA挂了,ServiceA仍在后台运行,直到ServiceA自己调用stopSelf()或者其他的组件手动调用stopService(ServiceA)时ServiceA才会停止。

1.2 BindService()模式

生命周期:onCreate()->onBind()->onUnBind()->onDestroy()

生命周期见上图右侧时序图。从名字上可以看出这个“绑定”一个服务,要创建一个支持绑定的Service,我们必须要重写它的onBind()方法,这个方***返回一个IBinder对象,它是客户端用来和服务器进行交互的接口。如ActivityA绑定ServiceA,此时ActivityA称为客户端,ServiceA称为服务端,ActivityA有从ServiceA返回的一个IBinder对象,通过该对象,ActivityA就可以调用ServiceA里面的方法。

生命周期受Activity影响,Activity结束,Service也就结束了

1.3 startService/bindService同时使用

我们完成可能startService启动一个服务的的同时又bindService绑定一个服务:

  • 因为通过startService启动,所以该Service将会一直再后台运行,onCreate方法只有在第一次startService时调用,onStartCommond的调用次数和startService调用次数一致
  • 这种情况需要同时stopService和unbindService方法, onDestroy方法才会被执行,缺一不可。

1.4 为什么bindService可以跟Activity生命周期联动?(附加题)

  1. bindService方法执行时,LoadedApk会记录ServiceConnection信息;
  2. Activity执行finish方法时,会通过LoadedApk检查Activity是否存在未注销/解绑的 ServiceConnection,如果有,那么会通知AMS注销/解绑对应的Service,并打印异常信息,告诉用户应该主动执行注销/解绑的操作。

1.5 在什么情况下使用 startService 或 bindService 或 同时使用startService 和 bindService?

  1. 如果你要启动的服务是需要后台长期运行,并且你经常需要使用到这个服务的,那么使用 startService 便可以了,比如我要经常上报数据到远程服务器,那么就用startService来启动负责数据上报的服务;
  2. 如果你只是要用到服务里的某个接口,使用频率也不高,因此不需要服务一直运行,而是等到需要使用接口的时候,通过bindService去绑定服务,创建该服务实例,调用完就解绑服务。这种方式可以节省很多系统资源,当然也要注意每次都要创建服务是需要花费一定时间的,只是相对于让服务一直运行后台,我们更愿意每次重新创建;
  3. 如果你想要与正在运行的 Service 取得联系,那么就可以通过 bindService来绑定服务,获取服务在本地的代理,也就是上面说的IBinder,通过IBinder来和Service取得联系。此时就同时使用 startService 和 bindService 了。

2 Service的种类和优先级

2.1 Service的种类

  1. 后台service
  2. 前台service
  3. 本地service
  4. 远程service
  • 按地点分:

    • 本地服务:该服务依赖于主进程,而非独立的进程,因此在一定程度节约了资源,且和主进程的通讯更简单,不过如果主进程挂了,该服务就终止。如一个主进程启动一个本地网络服务,将一些数据上报到远程服务器后,就可以停止该本地网络服务。
    • 远程服务:该服务独立于主进程,是一个独立的进程。不受其他进程的影响,多个进程都可以通过AIDL(这个后续会讲到,TODO)单独与该服务进行IPC通讯,因此较为复杂,且这种服务一般长期运行在后台。典型的例子就是系统服务。
  • 按运行类型分:

    • 后台服务:默认的服务都是后台服务,也就是startService()启动的服务,服务终止时用户是无法感知的,如天气更新等服务;
    • 前台服务:后台服务优先级较低,可能会被系统杀死,可以将后台服务提高为前台服务,是会在状态栏显示图标的服务,如常见的音乐播放服务,服务终止,状态栏的图标也消失;
    • 可交互的后台服务:服务本来是无法和用户交互的,但可以先用Activity和用户交互,同时Activity通过bindSerive()绑定服务,进而实现“可交互的后台服务”。

2.2 service的优先级

前台进程

可见进程

服务进程

后台进程

空进程

Service和Activity,一起看下Android中进程的优先级。以下优先级由高到低。

  1. 前台进程:

即与用户交互的Activity,以及Activity所使用的Service等,如果系统不足,最晚会杀死前台进程;

  1. 可见进程:

对用户可见,但不能与用户交互的Activity或者绑定在其上面的Service。

  1. 服务进程:

正使用StartService方法运行的服务,对用户是不可见的,但却是用户关心。例如正在看小说时,后台播放音乐的服务进程,如果此时系统需要空间运行,会先停止音乐服务,再停止看小说的进程。

  1. 后台进程:

运行着执行onStop方法而停止的程序,但是却不是用户当前关心的,例如后台挂着的QQ,这时的进程系统一旦没了有内存就首先被杀死

  1. 空进程:

不包含任何应用程序的进程,这样的进程系统是一般不会让他存在的。

3.3 如何保证service不被杀死

1、使用自定义的服务

2、使用两个service,当其中的一个service挂了,用另一个service启动

答案:

  1. 使用自定义系统服务:自定义系统服务原则上是不会被系统杀死的,因此,一些重要的服务可以考虑用自定义系统服务来实现;
  2. 使用系统服务来监控:先弄一个白名单,记录需要监控的应用的包名,再自定义一个系统服务,监控系统里某个应用被杀死,如果该应用在白名单上,则重新拉起应用;
  3. 设置为前台服务,前台服务的优先级更高,不会轻易被杀死;
  4. 使用第三方库来保活,如HelloDaemon;
  5. 使用双进程Service,在ServiceA和ServiceB相互保护,检测到有一个Service被杀死则重新拉起;
  6. 特殊操作拉起服务:在亮屏、开机等特殊操作手动的拉起服务;
  7. 使用闹钟循环拉起服务;
  8. 应用程序添加SystemUID,使应用程序成为系统应用,比如手机桌面的下拉框就是一个系统应用,叫做SystemUI;
  9. Service设置成START_STICKY(onStartCommand方法中),kill 后会被重启(等待5秒左右),重传Intent,保持与重启前一样;

3、说说Activity、Intent、Service 是什么关系?

Activity是属于页面的交互和展示,Intent可以在Activity中进行数据的传输,Service属于一种后台进程

4、直接在Activity中创建一个thread跟在service中创建一个thread之间的区别?

  1. Activity中创建:该线程负责完成该Activity的某个特殊的任务,特别是耗时的任务,等Activity销毁后,线程就没有意义了,也就销毁了
  2. Service中创建:因为Service是后台线程,只要服务没有挂掉,线程就可以一直运行

5 Service和Activity如何进行通信

  1. 在service中添加一个继承Binder的内部类,并添加相应的方法
  2. service中重写service的onbind方法,返回我们刚刚定义的那个内部类的实例
  3. Activity中绑定服务,重写serviceConnection,onserviceConnection时返回的IBinder调用逻辑方法

6 .IntentService是什么,IntentService原理,应用场景及其与Service的区 别

IntentService 是 Service 的子类,默认开启了一个工作线程HandlerThread,使用这个工作线程逐一处理所有启动 请求,在任务执行完毕后会自动停止服务。只要实现一个方法 onHandleIntent,该方法会接收每个启动请求的 Intent,能够执行后台工作和耗时操作。

可以启动 IntentService 多次,而每一个耗时操作会以队列的方式在 IntentService 的 onHandlerIntent 回调方法中 执行,并且,每一次只会执行一个工作线程,执行完第一个再执行第二个。并且等待所有消息都执行完后才终止服 务。

如何使用:

  1. 创建一个名叫 ServiceHandler 的内部 Handler
  2. 把内部Handler与HandlerThread所对应的子线程进行绑定
  3. HandlerThread开启线程 创建自己的looper
  4. 通过 onStartCommand() intent,依次插入到工作队列中,并发送给 onHandleIntent()逐个处理

应用场景:

后台下载任务,静默上传

7 bindService和startService混合使用的生命周期以及怎么关闭

  1. 如果先启动Bind service 后启动start Service

    生命周期:onCreate()->onStartCommand()->onBind()

  2. 如果先启动start Service后启动BindService

    生命周期:onCreate()->onBind()->onStartCommand()

  3. 如果只是stop Service

    service的onDestroy方法不会立即执行,在Activity退出的时候才会执行onDestroy()

  4. 如果只是unbindService

    只有onUnbind()方法会执行,ondestroy()不会执行

如果要完全退出Service,那么就得执行unbindService()以及stopService。