Physical Address:
ChongQing,China.
WebSite:
AIDL:Android Interface Definition Language,与其他IDL类语言类似,可以用于定义客户端、服务端均认可的远程调用接口(RPC)。AIDL基于binder实现跨进程的接口调用。在适用场景上是处理多线程、多客户端并发访问服务端的情况。通过AIDL规范,可以生成跨语言的、规范适用的中间代码,为IPC通信提供便利。安卓中除了AIDL,还有一套HIDL通信框架,不过其主要用于Native Service与HAL层之间的接口通信。
由于AIDL通信的底层仍是基于binder,所以仍然遵守C-S架构,其中提供接口供外部调用的我们称之为服务端,而调用方则为客户端。基于AIDL可以生成Java/C++/Rust的代码来加速我们的开发。
AIDL接口定义文件以.aidl形式结尾,允许通过一个或多个方法(可接收参数或返回值)来声明接口,参数和返回值可以为任意类型,甚至是AIDL定义的其他接口。AIDL的接口定义大致上基于Java语言,每个.aidl文件均需定义单个接口,并且只需要接口声明和方法签名。
包名:与Java语言类似,aidl中也需要定义包名,在生成对应的代码文件中会转换为命名空间;在定义包名时需要使用package关键字。如果我们需要导入其他包,则需要使用import关键字。
数据类型:AIDL中支持Java所有的原生数据类型,如byte、short、int、long、boolean等,也支持String、List、CharSequence、Map等复合类型。如果我们定义的AIDL接口是跨语言的,此处还需要额外注意数据类型的对齐,具体可参考下表:
Java/AIDL 类型 | C++ 类型 | NDK 类型 | Rust 类型 |
boolean | bool | bool | bool |
byte | int8_t | int8_t | i8 |
char | char16_t | char16_t | u16 |
int | int32_t | int32_t | i32 |
long | int64_t | int64_t | i64 |
float | float | float | f32 |
double | double | double | f64 |
String | android::String16 | std::string | String |
android.os.Parcelable | android::Parcelable | 不适用 | 不适用 |
IBinder | android::IBinder | ndk::SpAIBinder | binder::SpIBinder |
T[] | std::vector<T> | std::vector<T> | In: &[T] Out: Vec<T> |
byte[] | std::vector<uint8_t> | std::vector<int8_t>1 | In: &[u8] Out: Vec<u8> |
List<T> | std::vector<T>2 | std::vector<T>3 | In: &[T]4 Out: Vec<T> |
FileDescriptor | android::base::unique_fd | 不适用 | binder::parcel::ParcelFileDescriptor |
ParcelFileDescriptor | android::os::ParcelFileDescriptor | ndk::ScopedFileDescriptor | binder::parcel::ParcelFileDescriptor |
interface 类型 (T) | android::sp<T> | std::shared_ptr<T> | binder::Strong |
parcelable 类型 (T) | T | T | T |
union 类型 (T)5 | T | T | T |
T[N] 6 | std::array<T, N> | std::array<T, N> | [T; N] |
注释:在AIDL中对注释的使用同样遵循Java中使用注释的规则,使用//进行单行注释,使用/**/进行块注释。
注解:在AIDL中我们同样可以使用注解,AIDL中支持的注解参考该链接。
联合体:在Andrid12之后,可以在aidl中定义联合体,使用union关键字,例如:
package android.com.example;
import android.com.foo;
import android.com.bar;
union Settings
{
FooSettings settingfoo;
BarSettings settingbar;
@utf8InCpp String name;
}
枚举体:在Android11及更高版本中,支持枚举体定义,使用enum关键字,例如:
package android.com.example;
enum Boo {
A=1,
B=2,
C=3
}
结构体:结构体定义使用struct关键字,如下所示:
package android.hardware.automotive.evs@1.1;
/**
* Structure that describes informative events occurred during EVS is streaming
*/
struct EvsEventDesc {
/**
* Type of an informative event
*/
EvsEventType aType;
/**
* Device identifier
*/
string deviceId;
/**
* Possible additional information
*/
uint32_t[4] payload;
};
接口定义:在AIDL中我们主体部分是定义接口,此时我们需要使用关键字interface,并且确保接口名与文件名保持一致,如下所示:
//IHidlExample.aidl
package android.com.example;
interface IHidlExample {
void Foo(int para1);
}
常量:在AIDL接口中可以直接定义 String与int常量,如下所示:
package example.car.evs;
interface ICarEvsType {
const String EXAMPLE_CAR_EVS_INTERFACE_VERSION=”0.0.1”;
}
自定义通信数据:在AIDL中,如果需要在我们的接口中传递自定义数据,此时我们使用parcelable关键字,如下所示:
//ExampleMessage.aidl->ExampleMessage.java
package android.com.example;
parcelable ExampleMessage;
//ExampleMessage.aidl->ExampleMessage.h
package android.com.example;
parcelable ExampleMessage cpp_header "ExampleMessage.h";
在AIDL中定义接口时,有一些值得注意的点:
1.定义方法可以带零个或者多个参数、返回值或空值
2.所有非原生参数均需要指示数据走向的方向标记,这类标记可以是in、out或者inout;原生类型、String、IBinder以及AIDL生成的接口默认为in
基于不同的应用场景,AIDL支持不同的后端语言,目前包括Java/C++/Rust这三种语言,基于这三种语言细分为四种情况:
Backend | Language | API surface | Build systems |
Java | Java | SDK/SystemApi (stable*) | all |
NDK | C++ | libbinder_ndk (stable*) | aidl_interface |
CPP | C++ | libbinder (unstable) | all |
Rust | Rust | libbinder_rs (unstable) | aidl_interface |
以IRemoteService.aidl为例,生成的Java后端文件为:IRemoteService.java;在AOSP源码环境下我们可以通过aidl命令行工具来生成对应的后端文件;如下命令所示:
aidl --lang java IRemoteService.aidl --out .
如果我们需要在Android源码环境下通过Android.bp来生成对应的源码文件,可以使用aidl字段,参考如下:
java_library {
name: "aidlproxy",
//指定aidl所在路径
aidl: {
local_include_dirs: [
"aidl/Core",
],
},
//务必在src中进行囊括,这样才会导入aidl所生成的java源码
srcs: ["src/java/**/*.java"] + ["aidl/**/I*.aidl"],
}
在生成的IRemoteService.java文件内定义了一个名为IRemoteService的interface,其继承IInterface(android.os.IInterface),同时在该接口内部,还包含两个内部类,分别名为Default与Stub,如下所示:
package android.com.example;
import android.os.Binder;
import android.os.IBinder;
import android.os.IInterface;
import android.os.Parcel;
import android.os.RemoteException;
public interface IRemoteService extends IInterface {
//interface methods declaration
//Stub类
public abstract static class Stub extends Binder implements IRemoteService {
//asInterface方法,非常重要,用于获取将binder对象转换为java层的对象实例
public static IRemoteService asInterface(IBinder obj) {
}
//asBinder方法
public IBinder asBinder() { return this;}
//onTransact方法
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws
RemoteException {
}
// Proxy类
private static class Proxy implements IRemoteService {
}
}
//Default类
public static class Default implements IRemoteService {
}
}
同样是IRemoteService.aidl,生成的C++后端文件包括:IRemoteService.h\IRemoteService.cpp、BpRemoteService.h、BnRemoteService.h,在AOSP源码环境下我们可以使用aidl-cpp命令来手动生成。如果使用Android.bp在构建时生成,同样可以使用aidl配置字段,参考如下:
cc_binary {
//设定aidl文件的路径
aidl: {
local_include_dirs: [
"proxy/aidl/Core",
],
},
//在src中导入对应生成的源码
srcs: [
"proxy/aidl/Core/*.aidl",
]
}
生成的各个文件内容如下:
//BnRemoteservice.h
class BnRemoteService :public ::android::BnInterface<IRemoteService>
{
......
explicit BnRemoteService();
::android::status_t onTransact(uint32_t _aidl_code, const ::android::Parcel& _aidl_data, ::android::Parcel* _aidl_reply, uint32_t _aidl_flags) override;
};
//BpRemoteService.h
class BpRemoteService : public ::android::BpInterface<IRemoteService> {
public:
explicit BpRemoteService(const ::android::sp<::android::IBinder>& _aidl_impl);
virtual ~BpRemoteService() = default;
.....
};
//IRemoteService.h
class IRemoteService : public ::android::IInterface {
public:
DECLARE_META_INTERFACE(RemoteService)
.....
}; // class IRemoteService
class IRemoteServiceDefault : public IRemoteService {
public:
::android::IBinder* onAsBinder() override {
return nullptr;
}
.....
}
}; // class IRemoteServiceDefault
通常我们在C++侧实现aidl中所定义的接口时,需要继承BnRemoteService类。
Tips:如果我们不需要将自己的源码与aidl定义文件一起进行编译,而这些aidl定义可以作为一个独立的组件对外提供时,我们可以在Android.bp中使用aidl_interface字段来对一组aidl定义文件进行编译设定,使其编译成为独立的组件,如下所示:
aidl_interface {
name: "android.automotive.watchdog.internal",
unstable: true,
vendor_available: false,
srcs: [
"android/automotive/watchdog/internal/*.aidl",
],
backend: {
java: {
platform_apis: true,
enabled: true,
},
},
imports: [
"android.automotive.watchdog-V3",
],
}
这里我们讲讲服务实现的过程,以IRemoteService.aidl为例,其内容如下:
// IRemoteService.aidl
package com.example.android;
interface IRemoteService {
/** Request the process ID of this service, to do evil things with it. */
int getServicePid();
/**
* Returns a current status of service.
*/
int getCurrentStatus();
}
如果后端为Java服务,其服务实现需要完成如下内容:
1.实现服务类并继承自service,实现onBind接口
2.在服务类内部实现aidl中所定义的接口
如下代码所示:
public class RemoteService extends Service {
@Override
public void onCreate() {
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
// Return the interface
return binder;
}
private final IRemoteService.Stub binder = new IRemoteService.Stub() {
public int getServicePid(){
return Process.myPid();
}
public int getCurrentStatus() {
// Does nothing
}
};
}
如果后端为C++服务,其服务实现需要完成如下内容:
1.实现服务类并继承BnRemoteService类
2.实现相应的接口,并注册至serviceManager
如下代码所示:
#include <xxx/xxxxx/BnRemoteService.h>
class RemoteService : public BnRemoteService{
android::binder::Status getServicePid(int32_t* out) override{
//do something
}
android::binder::Status getCurrentStatus(int32_t* out) override{
//do something
}
}
关于C++服务注册到serviceManager的过程,可以参考我的这篇文章。
AIDL通信的本质仍旧是Binder通信,故而服务获取也通常通过ServiceManager进行获取。以下将分别以Java和C++为例,为大家举例说明如何获取服务。
Java:
import android.os.ServiceManager;
// registering
ServiceManager.addService("service-name", myService);
// getService for existing service
myService = IFoo.Stub.asInterface(ServiceManager.getService("service-name"));
// return if service is started now
myService = IFoo.Stub.asInterface(ServiceManager.checkService("service-name"));
// waiting until service comes up (new in Android 11)
myService = IFoo.Stub.asInterface(ServiceManager.waitForService("service-name"));
// waiting for declared (VINTF) service to come up (new in Android 11)
myService = IFoo.Stub.asInterface(ServiceManager.waitForDeclaredService("service-name"));
C++:
#include <binder/IServiceManager.h>
// registering
defaultServiceManager()->addService(String16("service-name"), myService);
// return if service is started now
status_t err = checkService<IFoo>(String16("service-name"), &myService);
// waiting until service comes up (new in Android 11)
myService = waitForService<IFoo>(String16("service-name"));
// waiting for declared (VINTF) service to come up (new in Android 11)
myService = waitForDeclaredService<IFoo>(String16("service-name"));
更多内容,后续进行完善~