整理一下AIDL相关的部分信息,也算是总结一下重新回顾一下知识吧~
什么是AIDL?
AIDL(Android Interface Definition Language) Android接口定义语言 利用它定义客户端与服务均认可的编程接口,以便二者使用进程间通信 (IPC) 进行相互通信。实际上起作用的并不是AIDL文件,而是根据AIDL生成的实例代码,AIDL是安卓替我们设计好的一个模板,根据模板生成Interface的代码。
ADIL的存在就是为了实现进程间的通信,通过AIDL我们可以在不同进程中进行数据通信与逻辑交互。
一般使用都是有两个端:客户端与服务端
支持参数类型
- 基本数据类型:boolean、byte、char、float、double、int、long
- String 、CharSequence
- List类型 (包含的数据必须是AIDL支持的类型或者其它声明的AIDL对象)
- Map类型(包含的数据必须是AIDL支持的类型或者其它声明的AIDL对象)
- 实现了Parcelable接口的数据类型 (例如:Bundle)
- AIDL接口本身
(网上人家说 不支持short,说是因为Parcel没有办法对short进行序列化,但是我发现里面的
writeValue()
中有short
的类型的处理。) 但是我一旦在AIDL
参数添加了short
类型的时候就一直报错再build
的时候就一直报错不行 Process 'command 'D:\Android\sdk\build-tools\28.0.3\aidl.exe'' finished with non-zero exit value 1 详细的报的是arguments 报错 可能是参数为short
的时候不支持 这个问题如果有懂的同学麻烦下面评论提及一下,大恩不言谢~~
AIDL的使用
先定义两端统一的包名跟接口aidl文件
IoneAidlInterface.aidl
: 暴露给客户端使用的接口类, (记得要导包,把数据类型或者引用到的实体类所在的包名明确标明导入!!)
IPersonBeanCallBack.aidl
: 在IoneAidlInterface.aidl
里面使用的回调对象声明类
PersonBean.aidl
: 在IoneAidlInterface.aidl
里面使用的实体类的一个声明文件,要声明了才能在aidl中使用,该实体类也必须实现接口parcelable
上面的都是aidl
格式的文件,下面是实现的PersonBean.kt
实体类文件
上面有定义的几个文件,
客户端
跟服务端
都要有并且包名必须要保持一致
创建服务端
AidlService
: 实现Service
在onbind()
的方法中将IOneAidlInterface.Stub()
的对象进行绑定。remoteCallbackList
则是用于注册回调使用的,callback
的注册跟取消注册在这里。
关键代码1:remoteCallbackList.getBroadcastItem(i)
用于获取回调对象, beginBroadcast
和 finishBroadcast
必须配套使用。
关键代码2:这个是当服务端这边自己被调用的函数出现问题,然后让客户端抛出java.lang.NullPointerException
的时候进行自己异常的捕获跟打印,不然客户端莫名其妙抛出,但是你服务端自己却没有捕获到异常定位不到异常的抛出真正地方(具体可看一下这篇文章)
- 在
AndroidManifest.xml
android:exported="true"
这个属性记得加上去,外部才能访问,并且添加<acition>
节点,调用的时候可以使用acition
来调用 ,当然你也可以直接指定Service
类名方式来绑定Service
创建客户端
关键代码1 中创建了 servirveConnection
对象,其并在匿名内部类中实现的onServiceConnected(name: ComponentName?, service: IBinder?)
中iOne = IOneAidlInterface.Stub.asInterface(service)
将iOne
实例化 ,最后关键代码3,将intent
的package
跟action
设置为服务端的service
配置信息,传入 bindService()
实现绑定。
以下调用,具体详情代码请看上面的几个类的方法中的具体逻辑
- 点击客户端
MainActivity
的按钮button1
服务端AidlService.kt
打印的信息
ok~~ 大功告成 基础的调用调通了~ 2. 客户端获取getPerson()
返回person
对象
客户端打印的信息:
- 注册回调 返回回调信息
3.1 客户端回调方法逻辑
3.2 客户端调用注册回调 调用setPerson
点击button3 注册回调 客户端Log:
服务端Log:
点击button4 iOne.person
触发服务端回调, 客户端Log:
服务端Log:
点击button5 注销回调 客户端Log:
服务端Log:
AIDL的回调
RemoteCallbackList
并不是一个List ,不能像 List 一样去操作它,遍历RemoteCallbackList
必须要以上面AidlService
类的使用方式进行,其中 beginBroadcast
和 finishBroadcast
必须配套使用,哪怕我们仅仅是想要获取 RemoteCallbackList 中的元素个数,
- 如果你不使用
beginBroadcast
,然后直接就去调remoteCallbackList.getBroadcastItem(0)
那么它会返回空给你 而不是你所期待的对象, - 如果你不使用
finishBroadcast
然后操作步骤绑定->使用->注销->使用
,就会抛异常说java.lang.IllegalStateException: beginBroadcast() called while already in a broadcast
!!!
定向TAG
android官网的文章上
All non-primitive parameters require a directional tag indicating which way the data goes . Either in , out , or inout . Primitives are in by default , and connot be otherwise . 所有的非基本参数都需要一个定向Tag来指出数据流通的方式,基本参数的定向Tag默认是并且只能是
in
in
:表示数据只能由客户端流向服务端out
:表示数据只能由服务端流向客户端inout
:表示数据可在服务端与客户端之间双向流通 数据流向是针对在客户端中的那个传入方法的对象而言的。in 为定向 tag 的话表现为服务端将会接收到一个那个对象的完整数据,但是客户端的那个对象不会因为服务端对传参的修改而发生变动;out 的话表现为服务端将会接收到那个对象的的空对象,但是在服务端对接收到的空对象有任何修改之后客户端将会同步变动;inout 为定向 tag 的情况下,服务端将会接收到客户端传来对象的完整信息,并且客户端将会同步服务端对该对象的任何变动。
AIDL鉴权
关于鉴权相关的可以看一下简书其它作者的这篇文章www.jianshu.com/p/69e5782dd…
注意事项(各种碰到的大坑小坑)
- 在导入以前可以运行的程序出现 AIDL 解析时已到达文件结尾,错误。 解决: AIDL 中有中文的注释。IDE升级到android studio 3.5以后就会出现这个错误。把所有aidl 的注释删除 运行就可以。
- 客户端调用远程服务端的方法时客户端线程会被挂起,如果远程的方法有长时间的耗时操作是会引起我们的客户端ANR现象的,这种情况就要避免在客户端UI线程中访问远程方法;
- 当服务端回调客户端的方法时,也要注意耗时的问题。
- 由于服务端的方法在服务端的Binder线程池中运行的,方法本身可以进行耗时操作,所以切记不要在服务端方法中开线程进行异步任务;
- Binder是会意外死亡的(比如服务被杀了,或者突然中断),每次Aidl意外断开都会调起linkToDeath和onServiceDisconnected方法,只是linkToDeath方法调用在onServiceDisconnected之前,(相关信息跟处理方法,可看看相关的参考有:www.jianshu.com/p/9af02aa66…)