AIDL 的研究
需求: Server端有个服务,client端需要使用Server 端的服务, 并且调用里面暴露的方法,实现双端通信。
一、 AIDL的创建步骤:
1: 先写服务端:server
在server的项目下新建一个aidl的文件,as会自动生成一个和java同目录的aidl的文件夹, 下层的目录两者是相同的:
1.png
创建了一个RemoteAidlInterface.aidl的接口
在这个接口里面我们定义了这样的一个远程方法:
2: 写一个服务端的服务:RemoteService
创建一个Binder继承RemoteInterfaceAidl.Stub() (问:这么创建的目的是什么, 继承了这个Stub可以达到什么目的)
3.png
那么现在服务端我们就写完了
3:现在另启客户端:client
首先把服务端的aidl复制到客户端,然后rebuild project
4.png
这个里面的内容跟服务端的一模一样
4:现在是Aidl已经被我们写好了,client如何远程调用服务端的服务呢?
调用服务使用bindService(intent, serviceConnection, flag)
intent 我们可以指向具体哪个服务, 我们在服务端会定义好具体的flag, 是用packageName和intentFilter可以指向这个服务。
我们该如何唤起这个服务, 并且调用其远程的callRemote()方法呢? 这个任务就理所应当的落在了ServiceConnection上面了
5.png
那么在serviceConnection 连接成功的时候, 我们将获取这个aidl的接口实例对象, 并且使用这个aidl的接口实力对象调用远程服务暴露的方法
整个使用的流程就这样的ending了
二、AIDL 的介绍
IPC : 跨进程的通信, 多进程之间的通信
1: java之中只有一个主线程,Android之中叫UI线程
2: Android在默认情况下,一个app占用一个进程, 也可能存在app在AndroidManifest中定义了process多进程模式去获取多份内存空间。
那么如果两个应用之间要获取彼此的数据,系统服务对系统中所有应用的启动和管理,那么这些都是需要使用跨进程的通信,也就是IPC通信
3: Android之中常见的IPC: Binder、 Socket、ContentProvider、文件共享等。 (问handle-message是否可以实现IPC)
4: AIDL: Android 接口定义语言,使用aidl可以实现发布和远程调用远程服务,实现跨进程通信。
5: AIDL支持定义的类型, 在demo中我们定义和调用的是String类型的,除此之外还支持其余的基本数据类型: int double float等
如果我们需要传递一个非基本数据类型,那么我们需要将这个数据实现序列化:
注意: 将对象的信息转成可以存储或者传输的形式的过程叫序列化
Serializable: Java标准的序列化接口
Parcelable: Android标准序列化接口
特点:
Serizlizable---使用简单,做数据的持久化方便存储,需要保存和或网络传输的时候选择Seriazliable。类只需要实现Seriazlianle接口, 并且提供一个可被序列化的版本id(serialVersionId)
Parcelable---是因为Serizlizable效率过慢而产生的,所以在内存间数据传递中使用Parcelable, 如activity的数传递等,使用比较复杂, 不同Android版本的parcelable不同,所以导致其在持久化存储中弱与Serizlizable。类需要实现Parcelable接口,并且一键生成writeToParcel, describeContent函数和CREATOR变量,将打包和解包的工作自己来定义,序列化操作由底层实现。
6:一个在同一个进程的抽象是Object, 不能被跨进程使用,如果需要跨进程,这需要依赖于Binder Framework, Android系统将一个可远程操作的定义为IBinder,任何一个可远程调用的对象都应该实现这个接口,但是因为他的高度抽象,涉及不少本地实现,所以只需要实现Binder这个抽象类就可以了,如果需要自定义实现,重写Binder中的onTransact()即可。
三、AIDL的逻辑处理分析:
1: 在make project之后, 在build/source下面会自动生成aidl对应的java文件,主要实现就是在这个Java文件里面
首先看一下跟client相关的Proxy:
1: 首先在Proxy中,传入了一个远程的IBinder, 重写了了aidl中的callRemote()方法,在写入参数之后, 将_data和_reply都传入IBinder的远程调用中, 并且在远程调用完成之后执行了_reply.readLong的操作
2:在远程调用的这句代码之中: mRemote.transact(Stub.TRANSACTION_callRemote, _data, _reply, 0); 第一个参数代表要执行的命令,data代表传入的数据, reply代表返回的数据,可以为null,最后一个参数是操作方式:O等待RPC的返回结果,1代表单向命令
2: 再看看Stub中的实现:
Stub本身继承了Binder抽象类,实现在服务端,以供客户端使用。在Stub中也有个transact的方法,他自己调用了服务端的callRemote,将计算出来的的结果写入了回传的Parcel去了(如果说远程服务方法中存在参数, 那么在Stub调用本身方法的之前会从data中读取对应的参数,用于调用计算reslut)
3: 从本质上来说:
1: client跟server的跨进程调用实际上是AIDL中proxy和Stub的数据传递的过程
2:Proxy继承Aidl接口远程调用服务,并且返回结果, 我们在client端bindService(serviceIntent, serviceConnection, flag) 绑定远程服务并调用,实际上是获取了AIDL中Proxy的实例去调用远程服务
3:AIDL的proxy调用远程接口,实际是通过binder的底层实现,将数据打包,使用binder的onTransact()方法,调用并且将参数传递到Stub, Stub再通过拆包得到相关的参数,然后调用server端的服务方法,将结果通过transact方法将result传回client端
4:Proxy是private权限的,这是避免外部对proxy的修改, 同时暴露了asInterface方法,可以拿到Proxy的实例。
9.png