专栏/Android 理解Binder机制(一) AIDL+Service的简单使用

Android 理解Binder机制(一) AIDL+Service的简单使用

2022年02月12日 15:17--浏览 · --喜欢 · --评论
粉丝:2348文章:74

本文主要分成以下几个部分:

  1. 简单介绍remote 和 local Service

  2. 介绍启动Service的两种api的异同

  3. AIDL + remote service的实践

Remote 和 Local Service

Android中存在两种Service,一种是local service,一种是remote service。remote service主要使用于进程间通信的场景。

进程之间的数据是被隔离的,不能相互访问。假设目前存在两个进程,分别是客户端和服务端。客户端进程需要获取某些数据,这些数据的处理逻辑只能由服务端来处理。在这种情况下,我们可以在服务端进程中定义一个Remote Service,通过服务端调用Remote Service中的方法的方式来返回数据。

AIDL的作用

上述的说法存在一个问题:因为客户端和服务端的内存空间是隔离的,从客户端的视角来看,它并不知道服务端中返回数据的逻辑。对于这个问题,我们可以使用【接口】来完成:只要服务端和客户端都持有这个【接口】,服务端中实现接口的业务逻辑,客户端中跨进程调用这个接口即可。

在这种情况下,Android提出了一种【接口】也就是AIDL,举个例子:

在里面定义了两个接口。AIDL会通过Android SDK生成同名的Java文件,这样就帮助了开发些写template code。具体的生成AIDL对应的Java文件的分析会在下一篇文章中给出。大概的类结构是这样:

启动两种API的异同

startService & stopService

这一对api需要传入一个intent即可

bindService & unbindService

这一对api的语义更强调【绑定】的性质,并且除了intent还需要传入一个serviceConnection,如果在组件被onDestroy时没有表用unbindService,会抛出异常。

因为bindService需要传入一个ServiceConnection,ServiceConnection需要实现的回调中函数onServiceConnected中会给出一个IBinder,所以我们可以借助这个IBinder做跨进程调用。

通过这两种方法表现的和Service周期变化如图:

不管调用哪个方法,都会调用Service的构造函数,但是后面走了不太一样的道路。需要有两条值得注意的地方:

  1. startService会调用onStartCommand回调。如果一个Service已经start,再调用startService,还会走一遍onStartCommand。

  2. bindService调用中,因为一个Service可能和多一个组件进行绑定,只有当绑定Service全都unbindService,才会调用Service#onUnbind回调。

AIDL + remoteService的实践

需求说明:

  1. 客户端App中需要请求服务端中定义的返回书籍Book list,同时可以addBook

  2. 服务端需要提供返回Book list和add Book的具体实现

服务端

Book对象

首先定义Book类型。这里需要注意,因为操作系统只能识别一些传输的基本类型,对于复合类型Book需要实现Parcelable才可以在跨进程中传输:

以上的代码需要注意实现:

  1. writeToParcel,即将数据写入Parcel中的方法。

  2. 需要提供解包的方法,这里具体是需要实现CREATOR对象,这个对象在AIDL生成的对应接口类中会有使用到。

我个人感觉在Parcel中存储的数据是通过一个native数组,将一些数据都连续地存在数组中。所以在CREATOR中createFromParcel也需要和writeToParcel对应的顺序读取(特别是对于同种类型的数据,因为不像一个Map,可以通过键值对取出)。

IBookManager.aidl

这个就是待会交给Android Studio 通过 make project会生成同名Java文件的【接口】:

这里需要注意,需要导入对应的Book类,同时这个Book类也需要写一个Book.aidl:

主要是指定Book是一个parcelable。

然后我们还需要定义一个Service,并且在AndroidManifest中注册。注册的时候记得写上intent-filter:

定义的Service具体是为了实现具体的业务逻辑:

之所以需要实现IBookManager.Stub是因为这个Stub才是涉及到具体的Binder相关的逻辑的类,这个后面文章再解释。

服务端就是这样:文件路径是这样:

客户端

客户端需要注意:

  1. 重新在Android Studio建立新的包,放置Book.java Book.aidl, IBookManager。包名不能错,否则会出现调用错误的interface异常,路径如下:

2. 主要就是调用就完事了哦,但是需要使用显式Intent。

在MainActivity中的代码如下:



投诉或建议