AIDL 中的注解

AIDL 支持注解,这些注解为 AIDL 编译器提供关于带注解元素的额外信息,这也会影响生成的桩代码。

语法与 Java 的语法类似

@AnnotationName(argument1=value, argument2=value) AidlEntity

在此,AnnotationName 是注解的名称,而 AidlEntity 是 AIDL 实体,例如 interface Foovoid method()int arg。注解会附加到紧随其后的实体。

某些注解可以设置括号内的参数,如上所示。不带参数的注解不需要括号。例如

@AnnotationName AidlEntity

这些注解与 Java 注解不同,尽管它们看起来非常相似。用户无法定义自定义 AIDL 注解;注解都是预定义的。某些注解仅影响特定后端,而在其他后端中不起作用。它们可以附加到的位置有不同的限制。

以下是预定义的 AIDL 注解列表

注解 在 Android 版本中添加
nullable 7
utf8InCpp 7
VintfStability 11
UnsupportedAppUsage 10
Hide 11
Backing 11
NdkOnlyStableParcelable 14
JavaOnlyStableParcelable 11
JavaDerive 12
JavaPassthrough 12
FixedSize 12
Descriptor 12

nullable

nullable 声明可能不提供带注解实体的值。

此注解只能附加到方法返回类型、方法参数和 parcelable 字段。

interface IFoo {
    // method return types
    @nullable Data method();

    // method parameters
    void method2(in @nullable Data d);
}

parcelable Data {
    // parcelable fields
    @nullable Data d;
}

注解不能附加到基本类型。以下是错误。

void method(in @nullable int a); // int is a primitive type

对于 Java 后端,此注解不起作用。这是因为,在 Java 中,所有非基本类型都按引用传递,这可能是 null

在 CPP 后端中,@nullable T 在 Android 11 或更低版本中映射到 std::unique_ptr<T>,在 Android 12 或更高版本中映射到 std::optional<T>

在 NDK 后端中,@nullable T 始终映射到 std::optional<T>

在 Rust 后端中,@nullable T 始终映射到 Option<T>

对于类似列表的类型 L(例如 T[]List<T>),@nullable L 映射到 std::optional<std::vector<std::optional<T>>>(或者,对于 Android 11 或更低版本的 CPP 后端,映射到 std::unique_ptr<std::vector<std::unique_ptr<T>>>)。

此映射有一个例外。当 TIBinder 或 AIDL 接口时,对于除 Rust 以外的所有后端,@nullable 均不起作用。换句话说,@nullable IBinderIBinder 同样都映射到 android::sp<IBinder>,后者本身是可为 null 的,因为它是一个强指针(CPP 读取仍然强制执行可为 null 性,但类型仍然是 android::sp<IBinder>)。在 Rust 中,只有使用 @nullable 注解的类型才是 nullable。如果使用注解,它们会映射到 Option<T>

从 Android 13 开始,@nullable(heap=true) 可用于 parcelable 字段,以对递归类型建模。@nullable(heap=true) 不能用于方法参数或返回类型。使用它进行注解后,该字段将映射到 CPP/NDK 后端中的堆分配引用 std::unique_ptr<T>@nullable(heap=true) 在 Java 后端中不起作用。

utf8InCpp

utf8InCpp 声明 String 在 CPP 后端中以 UTF8 格式表示。顾名思义,此注解对于其他后端不起作用。具体而言,String 在 Java 后端中始终为 UTF16,在 NDK 后端中始终为 UTF8。

此注解可以附加到可以使用 String 类型的任何位置,包括返回值、参数、常量声明和 parcelable 字段。

对于 CPP 后端,AIDL 中的 @utf8InCpp String 映射到 std::string,而没有注解的 String 映射到使用 UTF16 的 android::String16

请注意,utf8InCpp 注解的存在不会更改字符串在线传输的方式。字符串始终以 UTF16 格式在线传输。使用 utf8InCpp 注解的字符串在传输前会转换为 UTF16。收到字符串后,如果它被注解为 utf8InCpp,则会从 UTF16 转换为 UTF8。

VintfStability

VintfStability 声明用户定义的类型(接口、parcelable 和枚举)可以在系统域和供应商域之间使用。有关系统供应商互操作性的更多信息,请参阅HAL 的 AIDL

此注解不会更改类型的签名,但设置后,类型的实例将被标记为稳定,以便它可以跨供应商和系统进程传输。

此注解只能附加到用户定义的类型声明,如下所示

@VintfStability
interface IFoo {
    ....
}

@VintfStability
parcelable Data {
    ....
}

@VintfStability
enum Type {
    ....
}

当类型使用 VintfStability 进行注解时,类型中引用的任何其他类型也应使用此注解进行注解。在以下示例中,DataIBar 都应使用 VintfStability 进行注解。

@VintfStability
interface IFoo {
    void doSomething(in IBar b); // references IBar
    void doAnother(in Data d); // references Data
}

@VintfStability // required
interface IBar {...}

@VintfStability // required
parcelable Data {...}

此外,定义使用 VintfStability 注解的类型的 AIDL 文件只能使用 aidl_interface Soong 模块类型进行构建,并且 stability 属性设置为 "vintf"

aidl_interface {
    name: "my_interface",
    srcs: [...],
    stability: "vintf",
}

UnsupportedAppUsage

UnsupportedAppUsage 注解表示带注解的 AIDL 类型是非 SDK 接口的一部分,旧版应用可以访问该接口。有关隐藏 API 的更多信息,请参阅非 SDK 接口的限制

UnsupportedAppUsage 注解不会影响生成的代码的行为。此注解仅使用同名的 Java 注解来注解生成的 Java 类。

// in AIDL
@UnsupportedAppUsage
interface IFoo {...}

// in Java
@android.compat.annotation.UnsupportedAppUsage
public interface IFoo {...}

对于非 Java 后端,这是不起作用的。

Backing

Backing 注解指定 AIDL 枚举类型的存储类型。

@Backing(type="int")
enum Color { RED, BLUE, }

在 CPP 后端中,这将发出类型为 int32_t 的 C++ 枚举类。

enum class Color : int32_t {
    RED = 0,
    BLUE = 1,
}

如果省略注解,则假定 typebyte,这对于 CPP 后端映射到 int8_t

type 参数只能设置为以下整数类型

  • byte(8 位宽)
  • int(32 位宽)
  • long(64 位宽)

NdkOnlyStableParcelable

NdkOnlyStableParcelable 将 parcelable 声明(而非定义)标记为稳定,以便可以从其他稳定的 AIDL 类型引用它。这类似于 JavaOnlyStableParcelable,但 NdkOnlyStableParcelable 将 parcelable 声明标记为对于 NDK 后端稳定,而不是对于 Java 稳定。

要使用此 parcelable,

  • 您必须指定 ndk_header
  • 您必须有一个 NDK 库指定 parcelable,并且该库必须编译到库中。例如,在 cc_* 模块上的核心构建系统中,使用 static_libsshared_libs。对于 aidl_interface,在 Android.bp 中在 additional_shared_libraries 下添加库。

JavaOnlyStableParcelable

JavaOnlyStableParcelable 将 parcelable 声明(而非定义)标记为稳定,以便可以从其他稳定的 AIDL 类型引用它。

稳定的 AIDL 要求所有用户定义的类型都是稳定的。对于 parcelable,稳定要求其字段在 AIDL 源文件中显式描述。

parcelable Data { // Data is a structured parcelable.
    int x;
    int y;
}

parcelable AnotherData { // AnotherData is also a structured parcelable
    Data d; // OK, because Data is a structured parcelable
}

如果 parcelable 是非结构化的(或仅声明),则无法引用它。

parcelable Data; // Data is NOT a structured parcelable

parcelable AnotherData {
    Data d; // Error
}

当您引用的 parcelable 已经作为 Android SDK 的一部分安全可用时,JavaOnlyStableParcelable 允许您覆盖检查。

@JavaOnlyStableParcelable
parcelable Data;

parcelable AnotherData {
    Data d; // OK
}

JavaDerive

JavaDerive 在 Java 后端中为 parcelable 类型自动生成方法。

@JavaDerive(equals = true, toString = true)
parcelable Data {
  int number;
  String str;
}

此注解需要额外的参数来控制要生成的内容。支持的参数包括

  • equals=true 生成 equalshashCode 方法。
  • toString=true 生成 toString 方法,该方法会打印类型的名称和字段。例如:Data{number: 42, str: foo}

JavaDefault

Android 13 中添加的 JavaDefault 控制是否生成默认实现版本控制支持(对于 setDefaultImpl)。为了节省空间,默认情况下不再生成此支持。

JavaPassthrough

JavaPassthrough 允许使用任意 Java 注解来注解生成的 Java API。

AIDL 中的以下注解

@JavaPassthrough(annotation="@android.annotation.Alice")
@JavaPassthrough(annotation="@com.android.Alice(arg=com.android.Alice.Value.A)")

变为

@android.annotation.Alice
@com.android.Alice(arg=com.android.Alice.Value.A)

在生成的 Java 代码中。

annotation 参数的值会直接发出。AIDL 编译器不会查看参数的值。如果存在任何 Java 级语法错误,AIDL 编译器不会捕获到该错误,而是由 Java 编译器捕获到。

此注解可以附加到任何 AIDL 实体。对于非 Java 后端,此注解不起作用。

RustDerive

RustDerive 为生成的 Rust 类型自动实现特征。

此注解需要额外的参数来控制要生成的内容。支持的参数包括

  • Copy=true
  • Clone=true
  • Ord=true
  • PartialOrd=true
  • Eq=true
  • PartialEq=true
  • Hash=true

有关这些特征的说明,请参阅 https://doc.rust-lang.net.cn。

FixedSize

FixedSize 将结构化 parcelable 标记为固定大小。标记后,将不允许向 parcelable 添加新字段。parcelable 的所有字段也必须是固定大小的类型,包括基本类型、枚举、固定大小的数组以及使用 FixedSize 标记的其他 parcelable。

这不提供跨不同位宽的任何保证,并且不应依赖于混合位宽通信。

Descriptor

Descriptor 强制指定接口的接口描述符。

package android.foo;

@Descriptor(value="android.bar.IWorld")
interface IHello {...}

此接口的描述符是 android.bar.IWorld。如果缺少 Descriptor 注解,则描述符将为 android.foo.IHello

这对于重命名已发布的接口非常有用。使重命名的接口的描述符与重命名之前的接口的描述符相同,可以使这两个接口相互通信。

注释中的 @hide

AIDL 编译器识别注释中的 @hide,并将其传递到 Java 输出,以供 metalava 拾取。此注释确保 Android 构建系统知道 AIDL API 不是 SDK API。

注释中的 @deprecated

AIDL 编译器识别注释中的 @deprecated,作为标识不应再使用的 AIDL 实体的标记。

interface IFoo {
  /** @deprecated use bar() instead */
  void foo();
  void bar();
}

每个后端都使用后端特定的注解或属性来标记已弃用的实体,以便在客户端代码引用已弃用的实体时发出警告。例如,@Deprecated 注解和 @deprecated 标记会附加到 Java 生成的代码。