本文主要分成以下几个部分:
简单介绍remote 和 local Service
介绍启动Service的两种api的异同
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的构造函数,但是后面走了不太一样的道路。需要有两条值得注意的地方:
startService会调用onStartCommand回调。如果一个Service已经start,再调用startService,还会走一遍onStartCommand。
bindService调用中,因为一个Service可能和多一个组件进行绑定,只有当绑定Service全都unbindService,才会调用Service#onUnbind回调。
AIDL + remoteService的实践
需求说明:
客户端App中需要请求服务端中定义的返回书籍Book list,同时可以addBook
服务端需要提供返回Book list和add Book的具体实现
服务端
Book对象
首先定义Book类型。这里需要注意,因为操作系统只能识别一些传输的基本类型,对于复合类型Book需要实现Parcelable才可以在跨进程中传输:
以上的代码需要注意实现:
writeToParcel,即将数据写入Parcel中的方法。
需要提供解包的方法,这里具体是需要实现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相关的逻辑的类,这个后面文章再解释。
服务端就是这样:文件路径是这样:
客户端
客户端需要注意:
重新在Android Studio建立新的包,放置Book.java Book.aidl, IBookManager。包名不能错,否则会出现调用错误的interface异常,路径如下:
2. 主要就是调用就完事了哦,但是需要使用显式Intent。
在MainActivity中的代码如下: