Service的启动与绑定
2017-10-17 21:14
106 查看
前言
在 Android 开发的过程中,在很多的场景里面,都可以看到 Service 的身影。如此重要的一个 Android 控件,我们也需要了解它的种种的细节,这样我们才可以在开发的时候更加得心应手。Android 中的 Service 是什么?
在 Google 的官方文档中介绍到,Service 是一个应用组件,执行着在后台长期运行的操作,但是这些操作是不和用户直接进行交互的; 并且提供基本的功能给其他的应用去使用; 在我看来,服务 Service 可以看着一个没有界面的 Activity 吧,也就是用户不可以直接看到到服务它的样子是怎样的,但是可以通过其在后台提供的功能来感知服务的存在,而相对于 Activity 而言,它是用户直接看得到的,而且其提供的功能也可以直接通过 view 展示在用户的眼前。
1.0 Service 和 Activity 的关系
![](http://ouit3bg5b.bkt.clouddn.com/Selection_058.png)
从上面的 UML 图可以看出,Service 和 Activity 的继承关系,他们都最终继承于 Context 这个抽象类; 对于 Context 这个抽象类, 它是应用程序的全局信息的接口,它的实现是由 Android 系统提供,它可以访问特定于应用程序的资源和类,以及用于应用程序级的操作(例如启动活动、广播、接收 Intent 等)的呼叫。但是 Service 比 Activity 少继承了 ContextThemeWrapper 这个类。
2.0 Service 的两种启动方式以及对应的生命周期
2.1 以 startService() 方式启动 Service以 startService() 方式启动 Service 的流程图
![](http://ouit3bg5b.bkt.clouddn.com/Selection_065.png)
以 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 的示意图
![](http://ouit3bg5b.bkt.clouddn.com/Peek%202017-10-15%2021-37.gif)
2.2 以 bindService() 方式启动 Service
以 bindService() 方式启动 Service 流程图
![](http://ouit3bg5b.bkt.clouddn.com/Selection_067.png)
和
startService()相同,
bindService()方法同样是
Context抽象类里面的抽象类方法,对于以
bindService()方式启动的 Service,它的生命周期与启动者的生命周期有关,例如,一个在 Activity 里面通过
bindService()启动的 Service,如果 Activity 退出,该 Service 也会解除绑定而销毁,但需要注意的是,以这种方式销毁 Service 是会使程序报异常(Exception)的,异常如下图:
![](http://ouit3bg5b.bkt.clouddn.com/Selection_068.png)
这主要是一个服务连接溢出的异常。但是在程序报告完异常以后,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()方式启动,就显得十分重要了;
下面是混合式启动的示意图:
![](http://ouit3bg5b.bkt.clouddn.com/Peek%202017.10.17%2020-29.gif)
混合式启动的一般逻辑:
首先以
Content.startService()的方式启动,使得服务可以长期在后台运行;
然后以
Context.bindService() 的方式启动,以便获得操作服务里面方法的对象实例;
调用 unbindService()
的方式关闭服务,避免抛出服务溢出的异常;此时服务会长期在后台运行;
小结
这一篇博客主要基于 Google 文档的基础上面对 Service 的生命周期进行分析,主要分析了 Service 分别以Content.startService()和
Context.bindService()的异同点。下一篇,笔者将会和大家一起分析另一 Android 四大组件之一的 BroadCast。感谢你的阅读…
源码下载 StartAndBindServiceTest
相关文章推荐
- 如何在非Activity中启动、绑定Service(服务)
- 安卓:service,启动service和绑定service生命周期
- Service的启动与停止、绑定与解绑
- Framework学习(八)Service的启动/绑定过程
- 通过绑定的方式启动service
- 深入剖析Android四大组件(二)——Service服务之启动与绑定
- Service 启动和绑定流程
- Android中bindService的细节之一:从进程的角度分析绑定Service的流程【Service所在进程首次启动】
- 跨应用启动/绑定Service
- #Android学习#启动、绑定及跨应用启动Service
- Activity与Service的启动和绑定
- 安卓Service绑定方式启动服务并调用服务中的方法
- Android本地Service的启动与绑定
- android 音乐播放 启动方式 (1)利用内部类绑定service传送数据,只适用于单activity
- Service的启动、绑定,以及生命周期的笔记
- Service中绑定服务和启动服务的区别
- 深入剖析Android四大组件(二)——Service服务之启动与绑定
- Service绑定形式启动过程
- android89--Activity怎么和service绑定,怎么在activity中启动自己对应的service
- Android学习笔记(八)深入分析Service启动、绑定过程