AIDL整理

2020-11-07  本文已影响0人  Horps

Android Interface Definition Language即Android接口定义语言

我们知道,因为用户需要,Android系统中可以同时运行着多个进程,为了进程间能够正常的运行,进程之间必须保持独立互不干扰,这就是进程隔离。但是很多时候我们又不得不和另一个进程通信,那么这种进程间的通信就叫做IPC(Inter-Process Communication)。实现IPC的方式有很多种,像管道、信号、消息队列、共享内存、Socket等,RPC(Reomote Procedure Call,译称远程过程调用)也是IPC的一种方式,AIDL使用的就是这种方式。

binder-model

图片来源:Binder学习指南 (ps:这篇文章对Binder机制写的不错,值得一看)

简单来说,就是方法提供方称为server,方法调用方称为client,client和server之间的交互就是IPC。首先server注册到一个称为ServiceManager的地方,client调用时会去查询该server是否已经注册,若已注册则取到server的代理对象并执行要调用的方法,对于调用方来说就和调用自己进程中的方法一样,但是背后的工作确是由Binder驱动转交给真正拥有该方法的server去执行并把结果返回给调用方client。

我们还是从使用开始,一步步揭开AIDL的神秘面纱

在src/main下新建文件夹aidl,在其下新建一个.aidl文件,如果你是使用studio的创建AIDL文件功能选项创建的aidl文件,则会看到自动生成了一个basicTypes方法:

image-20200710151840622

这个方法里面的参数就是aidl支持的基本类型(你可以把这个方法删掉也不会有什么问题),除此之外,最好都需要import语句导入(List也可以不用导包,但是是建立在存在jdk的基础上),所以最规范的做法就是除基本类型之外的类型都要导入,和java语法一样,导入为止如下图:

image-20200710152247796

当需要自定义的数据类型时,首先要求这个自定义类型是parcelable类型,并实现Parcelable需要的所有东西,这里值得注意的是,因为AIDL规定除了基本数据类型之外,其他所有类型都需要定向tag显示声明,有in、out、inout三种,基本数据类型默认都是in(只能是in),后面会讲到其作用,这里我们只要记得如果要支持out或者inout定向的话要手动添加readFromParcel()方法,因为这个方法Parcelable接口中没有,需要自行添加:

image-20200710164356911

其次需要定义此类的aidl文件,这个aidl文件因为不是接口所以不会被自动生成(generated):

image-20200710153904493

特别注意: Demo02.aidl的包名要和Demo02.java的包名完全一致!!

然后在要使用这个类的aidl文件中引入:

image-20200710154018178

可以看到pringtList和addDemo02方法中的参数都不是基本类型,所以前面都必须有一个定向tag修饰(这里是in),那么in、out、inout的意义是什么呢?简单来说,根据前面我们提到的进程间通信的机制,通信的双方是client和server,当我们从client传递一个引用类型的数据给server端的时候,如果server端修改了数据,若定向是in,则client的数据不会被改变,若是out或者是inout则client端的数据也会跟着改变(比如说你调用test(a),如果服务端处理时a的某些属性值变了,那客户端的a对象的对应属性值也会跟着改变);同理如果定向修饰为out,则server端不会收到client发送的引用数据类型(比如说你调用test(a),服务端是不会使用这个a的,它只会重新创建一个同样类型的新对象)。这里可以发现,只有引用变量才可能被改变,因为引用数据类型有地址指向,所以这也就是为什么基本数据类型默认是in定向修饰符,而且只能是in,这里其实有一个readFromParcel的操作,后面会看到。关于定向tag,这篇文章讲的很详细:你真的理解AIDL中的in、out、inout么?

好了到现在为止我们已经做好了所有准备工作,现在只需要点击一下studio的rebuild project就会看到在对应文件夹中自动生成的java文件,我猜测studio版本和gradle版本不同,可能生成的目标目录也会不一样,所以不要死记这个路径,但肯定会在build/generated目录下:

image-20200710160104208

下面我们去看看给我们自动生成的java接口类:

1594368452548

可以看到我们的接口继承了一个android.os.IInterface接口:

image-20200710161039214

没什么特别含义,就是为了转化成IBinder类型的。

再来看内部,除了简单的声明了我们aidl中定义的接口方法之外,静态内部抽象类Stub是自动帮我们创建的内容,它继承了android.os.Binder并实现了外部的AIDL接口,它是AIDL的核心。

我们使用常见的Activity和Service的通信来梳理AIDL的工作,首先创建Service:

image

注意这里的Service可以被多个主题绑定,所以这里的方法要考虑线程安全,其次,因为binder是匿名类实现,同时,它又是私有变量只通过onBind方法暴露,所以这里自定义的方法不能够被外部调用。

再来看一下client端关键代码:

image-20200712091343773

mAidlService是一个AidlTest02类型的变量,在service绑定成功之后,通过AidlTest02.Stub.asInterface(IBinder)方法赋值,我们看看这个方法:

image-20200712092257714

这里出现了两个return分支,本地调用返回和远程调用返回。

总结

到此为止,我们顺利地梳理了一遍AIDL从创建aidl文件到使用AIDL机制来实现跨进程通信地流程,不过其中还有bindService方法之后Android框架做了什么、onTransact方法中return true之后Binder是如何去做的问号,后面有时间会研究一下。

上一篇 下一篇

猜你喜欢

热点阅读