Service的启动与绑定

来源:互联网 发布:中航显示屏软件下载 编辑:程序博客网 时间:2024/05/18 00:15

前言

在 Android 开发的过程中,在很多的场景里面,都可以看到 Service 的身影。如此重要的一个 Android 控件,我们也需要了解它的种种的细节,这样我们才可以在开发的时候更加得心应手。

Android 中的 Service 是什么?

在 Google 的官方文档中介绍到,Service 是一个应用组件,执行着在后台长期运行的操作,但是这些操作是不和用户直接进行交互的; 并且提供基本的功能给其他的应用去使用; 在我看来,服务 Service 可以看着一个没有界面的 Activity 吧,也就是用户不可以直接看到到服务它的样子是怎样的,但是可以通过其在后台提供的功能来感知服务的存在,而相对于 Activity 而言,它是用户直接看得到的,而且其提供的功能也可以直接通过 view 展示在用户的眼前。

1.0 Service 和 Activity 的关系

Service和Activity的关系图

从上面的 UML 图可以看出,Service 和 Activity 的继承关系,他们都最终继承于 Context 这个抽象类; 对于 Context 这个抽象类, 它是应用程序的全局信息的接口,它的实现是由 Android 系统提供,它可以访问特定于应用程序的资源和类,以及用于应用程序级的操作(例如启动活动、广播、接收 Intent 等)的呼叫。但是 Service 比 Activity 少继承了 ContextThemeWrapper 这个类。

2.0 Service 的两种启动方式以及对应的生命周期

2.1 以 startService() 方式启动 Service

以 startService() 方式启动 Service 的流程图

以startService()方式启动Service

以 startService() 方式启动 Service,系统就会回调 onCreate() 方法和 onStartConnect() 方法,如果不先调用 stopService() 关闭 Service,就直接再次调用 startService() 方法启动 SerVice,onCreate() 方法不会再次被调用,但是 onStartConnect() 方法会随着 startService() 被调用多少次而回调多少次; 还有一点需要注意的是,Service 以 startService() 方式启动,但在启动它的组件退出前,没有调用 onStopService() 方法停止服务,服务就会在后台一直运行,直到用户通过查看进程的方式把服务杀死,或者在系统内存运行紧张的时候,系统强行把服务杀死; 并且不管调用多少次 startService() 方法去启动服务,最后只需要调用一次 stopService() 方法就把服务给停止了。
对于 stopService() 方法有点需要注意的是,如果已经调用该方法的 Service 仍然有 ServiceConnection 对象绑定,并且是设置 BIND_AUTO_CREATE 这个 flag 的,该 Service 直到这些绑定都已经被移除,才会被销毁。

以 startService() 方式启动 Service 的示意图

2.2 以 bindService() 方式启动 Service

以 bindService() 方式启动 Service 流程图

以bindService()方式启动Service流程图

startService() 相同, bindService() 方法同样是 Context 抽象类里面的抽象类方法,对于以 bindService() 方式启动的 Service,它的生命周期与启动者的生命周期有关,例如,一个在 Activity 里面通过 bindService() 启动的 Service,如果 Activity 退出,该 Service 也会解除绑定而销毁,但需要注意的是,以这种方式销毁 Service 是会使程序报异常(Exception)的,异常如下图:

这主要是一个服务连接溢出的异常。但是在程序报告完异常以后,Service 还是会执行 onUnbind() 方法和 onDestroy() 方法的。

现在主要介绍 Context.bindService(Intent, ServiceConnection, int) 这个方法。该方法是位于 Context 抽象类里面,是一个抽象方法。这个方法需要传进三个参数:

参数 Intent 定义需要连接启动的 Service,可以通过隐式意图启动 Service (如 action、category、etc),也可以通过显式意图启动服务(也就是直接指定 Service 的名字)。 ServiceConnection 这是一个监听函数,主要负责监听通过 bindService() 方式启动的 Service 的启动与停止的状态,当分别发生这两个状态的时候,会分别回调 onServiceConnected(ComponentName, IBinder)onServiceDisconnected(ComponentName) 的方法。这个参数不可以为空,也就是不可以传空对像进去 int 传进去的是 Context 函数里面的一个常量,定义通过何种方式绑定 Service

对于 Context.bindService(Intent, ServiceConnection, int) 函数里面的 int 参数(翻译自Google):

int BIND_AUTO_CREATE 只要绑定存在,就自动创建服务。请注意,虽然这将创建服务,但其 onStartCommand(Intent, int, int) 方法仍将仅由于显式调用而被调用 startService(Intent)。即使没有这种情况,在服务创建时,这仍然为您提供对服务对象的访问。请注意,在 ICE_CREAM_SANDWICH 不提供此标志之前,也会影响系统考虑目标服务进程的重要性。当设置时,提升它的唯一方法是通过绑定服务,在这种情况下,只有当该活动处于前台时才会很重要。现在要实现这个行为,你必须明确提供新的标志 BIND_ADJUST_WITH_ACTIVITY。为了兼容性,未指定的旧应用程序BIND_AUTO_CREATE 将自动为其设置标志 BIND_WAIVE_PRIORITY并 BIND_ADJUST_WITH_ACTIVITY 为其设置,以获得相同的结果。 BIND_DEBUG_UNBIND 用于解除绑定的不匹配调用的调试帮助。当设置此标志时,unbindService(ServiceConnection) 保留以下呼叫的呼叫堆栈, 如果稍后发出不正确的取消绑定呼叫,则打印。请注意,这样做需要保留关于应用程序生命周期的绑定的信息,导致泄漏 - 这只能用于调试。 BIND_NOT_FOREGROUND 不允许此绑定将目标服务的进程提升到前台调度优先级。它仍将被提升至与客户端至少相同的内存优先级(以便在任何客户端不可杀死的情况下,其进程将不会被杀死),但是对于CPU调度,它可能会留在后台。这仅对绑定客户端是前台进程并且目标服务处于后台进程的情况有影响。 BIND_ABOVE_CLIENT 表示绑定到此服务的客户端应用程序认为服务比应用程序本身更重要。当设置时,平台将尝试让内存不足的杀手杀死应用程序,然后杀死它所绑定的服务,尽管不能保证是这样。 BIND_ALLOW_OOM_MANAGEMENT 允许托管绑定服务的进程经历其正常的内存管理。它将被视为更像一个正在运行的服务,允许系统(临时)清除进程,如果内存不足或其他可能有的其他想法,并且更积极地使其成为候选人被杀死(并重新启动)如果运行了很长时间。 BIND_WAIVE_PRIORITY 不影响目标服务主机进程的调度或内存管理优先级。允许服务的进程在背景LRU列表上进行管理,就像后台的常规应用程序进程一样。

3.0 混合式启动与关闭Service

  • 以 Content.startService() 方式启动的 Service,启动的组件不可以调用 Service 里面的方法,但是以这种方式启动的 Service,会长期运行在系统的后台;
  • 以 Context.bindService() 方式启动的 Service,启动的组件可以通过 Binder 调用 Service 里面的方法,但是以这种方式启动的 Service,其生命周期与启动其的组件的生命周期相关;
如果有这么一个情景:

需要一个长期运行在后台的服务,同时需要调用服务里面的方法,应该怎么启动 Service ? 例如音乐播发器?

这时候,就需要涉及到 Service 的混合式启动了。先以 Content.startService() 方式启动,还是先 Context.bindService() 方式启动,就显得十分重要了;

下面是混合式启动的示意图:

混合式启动的一般逻辑:

  • 首先以 Content.startService() 的方式启动,使得服务可以长期在后台运行;
  • 然后以 Context.bindService() 的方式启动,以便获得操作服务里面方法的对象实例;
  • 调用 unbindService() 的方式关闭服务,避免抛出服务溢出的异常;
  • 此时服务会长期在后台运行;

小结

这一篇博客主要基于 Google 文档的基础上面对 Service 的生命周期进行分析,主要分析了 Service 分别以 Content.startService()Context.bindService() 的异同点。下一篇,笔者将会和大家一起分析另一 Android 四大组件之一的 BroadCast。感谢你的阅读…


源码下载 StartAndBindServiceTest

原创粉丝点击