有些书在介绍和讲解 android 的 Service 组件时,会使用后台服务一词,并且与运行在主线程的 Activity 相对。因为后台一词很容易误解,服务一直运行在后台?什么线程在运行?服务一直有条线程在运行?而且后台在不同语境有不同理解。但是如果你开发过 COM 应用的话,这个 Service 组件就很容易理解。
我们回顾是如何使用 COM 的,先实例一个 CoInstance,然后通过 IUnknown 接口 QueryInterface 查询出我们想要的接口,然后调用接口的方法访问 COM 组件提供的服务。COM 组件可能实例在同一进程,又或者实例在进程外的本地进程(或者远程进程)。对于自由线程的进程内组件,接口调用(COM 组件的代码)运行在调用线程。对于套间线程的组件,不论是进程内还是进程外,接口调用都通过 IPC(LPC 或 RPC)串行访问 COM 组件的功能。
我们来看一下 android 中 Service 的使用。
对于非系统服务的使用。
首先 Context.bindService,(如果系统范围内没有服务实例则被创建),他们说绑定到一个服务,其实最重要是获取 Service 对应的 IBinder。在这里要习惯 Java 或 JavaScript 编程中常用到的接收结果的类,这些类往往通过提供接口回调方法来接收结果。像这里的第二个参数是一个 ServiceConnection 类,就这样一看还以为它负责处理 connect 功能相关的方法,但实质上只是一个用来接收结果的回调接口。哎!没什么好说,要习惯它。
得到的这个 IBinder 就是我们要访问的关键,但是怎么访问,一般的书和帖都没有说明。我通过系统服务 ClipboardManager(只是随手选中)代码来举例。在源代码的注释中,android 团队用 proxying 来表述这种通过 ClipboardManager 对 IClipboard 接口照样封装一次,类似桥模式的访问。
对于系统服务的使用。
首先 Context.getSystemService,我们得到系统服务接口的一个 proxying 封装。对于举例的 ClipboardManager 封装的就是对 IClipboard 接口的调用。
我们进入 ClipboardManager 源代码。
- static private IClipboard getService() {
- synchronized (sStaticLock) {
- if (sService != null) {
- return sService;
- }
- IBinder b = ServiceManager.getService("clipboard");
- sService = IClipboard.Stub.asInterface(b);
- return sService;
- }
- }
可以看出,还是要得到服务的 IBinder。我们可以会意一笑,IClipboard.Stub.asInterface(b),不就相当于对 IUnknown 进行 QueryInterface 吗?好了,IBinder 就相当于一个 IUnknown,通过 "目标接口. Stub.asInterface" 查询目标接口,然后当然就是对不为空的接口进行访问了,访问也就是服务的提供的功能。
对于 android 来说,服务是一个实例,是系统范围的单例,跟服务同进程的调用运行在调用线程(也可以是主线程),在服务进程外的调用就是一种 ActiveObject 模式的访问,服务功能调用运行在服务进程内,接口调用进程和服务进程通过 IPC 传递参数和结果。 通过浏览 IBinder 的实现 Binder 的源代码,大部分功能在做 ipc 和 proxy 的工作。
对于 Service 除了像 COM 组件那样通过提供接口访问功能外,另一种途径就是处理 Intent 请求,这个处理就在 Service.onStartCommand 中进行,并不用借助 IBinder。访问服务的功能通过 startService(Intent) 来请求一次访问,再一次吐槽,这个命名怎么都会字面理解成启动服务,其实不然,主要是通过 Intent 传递参数,请求一次服务。并没有 Service.onPause 和 Service.onStop,也就是 Service.onStart 并不影响 Service 的状态,单单只是处理 Intent 的回调入口,哎~。PendingService 则有一专用线程,通过消息方式回调 PendingService.onHandleIntent 串行处理所有传递进来的 Intent。
另外 Context.bindService 也带参数 Intent,并非要在 Service.onBind 中处理 Intent 请求,而是作为接口查询参数,返回不同的 IBinder,你的 Service 可以聚集多个 IBinder。
来源: