以 AIDL 编写的注释

AIDL 支持通过添加注释向 AIDL 编译器提供所注释元素的其他信息,这些信息也会影响生成的桩代码。

其语法类似于 Java 的语法:

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

其中 AnnotationName 为注释的名称,AidlEntityinterface Foovoid method()int arg 等 AIDL 实体。注解会附加到其后面的实体。

如上所示,您可在括号内为某些注释设置参数。注释如果没有参数,就不需要使用括号。例如:

@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>

在 NDK 后端,@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 接口时,@nullable 对所有后端(Rust 除外)都是空操作。换言之,@nullable IBinderIBinder 都会映射到 android::sp<IBinder>(已为 nullable,因为它是强指针)(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>。在 Java 后端,@nullable(heap=true) 是空操作。

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 {...}

此外,只能使用 aidl_interface Soong 模块类型构建用于定义附有 VintfStability 注释的类型的 AIDL 文件,并将 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,
}

如果省略该注释,type 将被假定为 byte,后者会在 CPP 后端映射到 int8_t

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

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

NdkOnlyStableParcelable

NdkOnlyStableParcelable 用于将 Parcelable 声明(未定义)标记为“稳定”,以供其他稳定的 AIDL 类型引用。 这与 JavaOnlyStableParcelable 类似,但 NdkOnlyStableParcelable 用于针对 NDK 后端(而非 Java)将 Parcelable 声明“稳定”。

如需使用此 Parcelable,请执行以下操作:

  • 您必须指定 ndk_header
  • 您必须有一个用于指定 Parcelable 的 NDK 库,且必须将该库编译到内容库中。例如,在核心构建系统内的 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)")

会在生成的 Java 代码中变为

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

annotation 参数的值会直接发出。AIDL 编译器不会检查参数的值。如果存在 Java 级语法错误,AIDL 编译器无法发现该错误,但 Java 编译器可以发现。

可将该注释附加到任一 AIDL 实体。在非 Java 后端,该注释为空操作。

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 生成的代码。