AIDL简介
AIDL:AIDL=Android Interface definition language(Android 接口定义语言),与您可能使用过的其他 IDL 类似。 您可以利用它定义客户端与服务使用进程间通信 (IPC) 进行相互通信时都认可的编程接口。 在 Android 上,一个进程通常无法访问另一个进程的内存。 尽管如此,进程需要将其对象分解成操作系统能够识别的原语,并将对象编组成跨越边界的对象。 编写执行这一编组操作的代码是一项繁琐的工作,因此 Android 会使用 AIDL 来处理。
摘自google开发者文档:https://developer.android.com/guide/components/aidl.html?hl=zh-cn
AIDL的作用
- 用一套固定的模式,帮助android开发者实现跨进程(RPC)调用
- 自动生成基于client & server端所需要的代码
- 底层基于Binder实现
最重要的是方便,同时通过编译工具生成对应的代码,极大简化了java码农进行RPC开发
AIDL的一些基本ABC
google开发者文档已经介绍的非常详细了,这边稍微摘录一些。
传送门:https://developer.android.com/guide/components/aidl.html?hl=zh-cn#CreateAidl
1. 创建 .aidl 文件
AIDL 使用简单语法,使您能通过可带参数和返回值的一个或多个方法来声明接口。
参数和返回值可以是任意类型,甚至可以是其他 AIDL 生成的接口。
您必须使用 Java 编程语言构建 .aidl 文件。每个 .aidl 文件都必须定义单个接口,并且只需包含接口声明和方法签名。
以下是一个 .aidl 文件示例:
// IRemoteService.aidl
package com.example.android;
// Declare any non-default types here with import statements
/** Example service interface */
interface IRemoteService {
/** Request the process ID of this service, to do evil things with it. */
int getPid();
/** Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
}
2. 实现接口
当您开发应用时,Android SDK 工具会生成一个以 .aidl 文件命名的 .java 接口文件。生成的接口包括一个名为 Stub
的子类,这个子类是其父接口的抽象实现,用于声明 .aidl 文件中的所有方法。
以下是一个使用匿名实例实现名为 IRemoteService 的接口(由以上 IRemoteService.aidl
示例定义)的示例:
private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
public int getPid(){
return Process.myPid();
}
public void basicTypes(int anInt, long aLong, boolean aBoolean,
float aFloat, double aDouble, String aString) {
// Does nothing
}
};
- 在实现 AIDL 接口时应注意遵守以下这几个规则:
- 由于不能保证在主线程上执行传入调用,因此您一开始就需要做好多线程处理准备,并将您的服务正确地编译为线程安全服务。
- 默认情况下,RPC 调用是同步调用。如果您明知服务完成请求的时间不止几毫秒,就不应该从 Activity 的主线程调用服务,因为这样做 可能会使应用挂起(Android 可能会显示“Application is Not Responding”对话框)— 您通常应该从客户端内的单独线程调用服务。
- 您引发的任何异常都不会回传给调用方。
3. 向客户端公开该接口
您为服务实现该接口后,就需要向客户端公开该接口,以便客户端进行绑定。 要为您的服务公开该接口,请扩展 Service 并实现 onBind(),以返回一个类实例,这个类实现了生成的 Stub(见前文所述)。以下是一个向客户端公开 IRemoteService 示例接口的服务示例。
public class RemoteService extends Service {
@Override
public void onCreate() {
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
// Return the interface
return mBinder;
}
private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
public int getPid(){
return Process.myPid();
}
public void basicTypes(int anInt, long aLong, boolean aBoolean,
float aFloat, double aDouble, String aString) {
// Does nothing
}
};
}
不一样的AIDL分析
上一章节的内容是最基本的AIDL使用守则,在进入本章节前请确保已经了解了AIDL/Binder的基本用法,并且知道进程和调试的基本信息。
基于上述的:IRemoteService
,我们写了一个hellowrold
IDE工具的编译
如果是使用android studio进行aidl编译的话,可以从compileDebugAidl
看出对应的编译指令:
D:\software\AndroidStdio\sdk\build-tools\25.0.2\aidl.exe
-pD:\software\AndroidStdio\sdk\platforms\android-25\framework.aidl
-oZ:\code\demo\app\build\generated\source\aidl\debug
-IZ:\code\demo\app\src\main\aidl
-IZ:\code\demo\app\src\debug\aidl
-IZ:\code\demo\app\build\intermediates\exploded-aar\com.android.support\appcompat-v7\25.3.0\aidl
-IZ:\code\demo\app\build\intermediates\exploded-aar\com.android.support\support-v4\25.3.0\aidl
-IZ:\code\demo\app\build\intermediates\exploded-aar\com.android.support\support-fragment\25.3.0\aidl
-IZ:\code\demo\app\build\intermediates\exploded-aar\com.android.support\support-media-compat\25.3.0\aidl
-IZ:\code\demo\app\build\intermediates\exploded-aar\com.android.support\support-core-ui\25.3.0\aidl
-IZ:\code\demo\app\build\intermediates\exploded-aar\com.android.support\support-core-utils\25.3.0\aidl
-IZ:\code\demo\app\build\intermediates\exploded-aar\com.android.support\animated-vector-drawable\25.3.0\aidl
-IZ:\code\demo\app\build\intermediates\exploded-aar\com.android.support\support-vector-drawable\25.3.0\aidl
-IZ:\code\demo\app\build\intermediates\exploded-aar\com.android.support\support-compat\25.3.0\aidl
-dC:\Users\AppData\Local\Temp\aidl6189821749730508340.d
Z:\code\demo\app\src\main\aidl\android\example\com\IRemoteService.aidl
因此,其实是IDE工具帮忙做了AIDL文件的转换,如果有时候AIDL编译出错,也可以手动调用IDE工具自行生成或者是查看log定位问题。
阅读auto-generated AIDL
前面有说到,AIDL文件会通过IDE工具生成java文件。
这个文件一般会生成在out/build目录下,文件名相同,只是后缀会改成java。
例如,demo中的文件目录:app\build\generated\source\aidl\debug\com\example\android\IRemoteService.java
IRmoteService.java
整体上还是保存了IRemoteService.aidl
的面貌
如图所示,我们折叠了Stub
部分的实现,先看IRemoteSerivce
的本体:
- IRmoteService继承了
android.os.IInterface
,这个接口提供了IBinder asBinder();
的能力根据我们对java层Binder的认识,可以了解到这个
asBinder()
绝大对数都是提供了一个Proxy
即,client端的app是通过Proxy来与远端的实体Server建立联系
下面我们展开Stub
看看具体的实现:
- Sub继承自
android.os.Binder
,因此会具备RPC能力,其中asBinder
会决定具体的IBinder对象是谁 asInterface
会负责做强制转换,目的是把各类IBinder对象转换成具体的子类asInterface
有一些比较奥妙的地方,详细请参考:CSDN-Binder中的asInterface解析
- 借助
Proxy
内部类,返回对应的IRemoteService对象 Stub
内部再次嵌套了一个Proxy
类,后面再次展开了看asBinder
的庐山真面目,这里直接return this
,也即Stub
本身即是Binder对象,也是Server实体onTransat
这个函数是典型的Binder通讯专有api,而onTransat
通常是Server端
综上所述,我们还得再看一眼Proxy
:
- Porxy实现了
IRemoteService
的接口,这一点与Stub
无异,但是Stub是继承自android.os.Binder
- 针对每一个具体的api,比如
getPid
而言,其实都是通过mRemmote
重新包装了一下,并透过transact
转发那么问题就来了,这边
transact
会到哪里呢?
关于上面的问题可以参考:transact和onTransact的区别
小结:
- AIDL->Java,IDE会帮我们完成,真正参与编译的还是java代码
- 同名的AIDL文件,会带有3个class,即
IRemoteService
,Stub
,Proxy
Stub
是transact的受理方,所以它是服务(Server),它需要实现onTransact
,它继承自Binder
,并需要实现IXXXX
定义的apiProxy
是transact的申请方,所以它是代理,是客户端(Client),它拥有IBinder,仅实现了IXXXX
定义的api
最后,列一下对应的class关系UML图:
从图中,还有两个隐藏的点
Stub
是abstract的类,所以它需要被继承Proxy
是具类,所以可以直接使用
调试
- 你真的了解了AIDL的生成以及上文提到的transact以及onTransact的关系。
- 你真的了解对应代码运行的环境和所处的进程
结合《Android Frameworks代码调试》,相信debug起来是非常简单的。
一般来说,我们在debug AIDL文件的时候,需要把对应生成的java文件一并放到IDE中,这个方法有很多,直接裸打开也是可以的。
然后找到对应的进程,通过debug attach上进程,找到对应地方打上断点。
如果你发现断点不生效,那么一定是进程attach错了,所以回到上文
- 你真的了解对应代码运行的环境和所处的进程 ?!!
一点小扩展
如果你真的懂了上面所说的,那我们来找个例子。
以AMS为例,其中startActivity
的一个调用流程。
AMS有一个特点,虽然它没有对应的AIDL,但是整段代码其实是由AIDL转换为JAVA后的JAVA最终形态。
也就是说,AMS是裸写的一个Binder RPC。我们通过UML图来看看AMS相关的类是什么关系。
- 结合之前提到的那些点,我们来看看
- 首先
IActivityManager
,可以认为是之前的IRemoteService.aidl
生成的IRemoteService
类 - 其次,看继承关系数,
ActivityManagerNative
继承了Binder
,同时还实现了IActivityManager
这不妥妥的就是
IRemoteService.Stub
吗? - 对应的,
ActivityManagerProxy
则是直接实现了IActivityManager
这不妥妥的就是
IRemoteService.Proxy
吗?
如果要debug整个startActivity
你要怎么做呢?
首先,我们确认了整个startActivity
是从app端发起的,最后是在AMS内部处理的。
所以我们打断点的时候,java层app的终点,是在ActivityManagerProxy
中的:
对应的,server端的起点,是在ActivityManagerNative
的onTransact
中的: