AIDL 中的註解

AIDL 支援註解,可為 AIDL 編譯器提供有關註解元素的額外資訊,這也會影響產生的 Stub 程式碼。

語法與 Java 類似:

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

其中 AnnotationName 是註解的名稱,AidlEntity 則是 AIDL 實體,例如 interface Foo、void method() 或 int arg。註解會附加至後續的實體。

部分註解可在括號內設定引數,如上一個範例所示。沒有引數的註解不需要括號。例如:

@AnnotationName AidlEntity

這些註解與 Java 註解類似,但兩者並不相同。所有註解都是預先定義,且有附加位置的限制。部分註解只會影響特定後端,在其他後端中則為無運算。

以下是預先定義的 AIDL 註解清單:

註解在 Android 版本中新增
nullable7
utf8InCpp7
VintfStability11
UnsupportedAppUsage10
Hide11
Backing11
NdkOnlyStableParcelable14
JavaOnlyStableParcelable11
JavaDerive12
JavaPassthrough12
FixedSize12
Descriptor12

可為空值

nullable 宣告註解實體的值可為空值。

您只能將這個註解附加至方法回傳型別、方法參數和可封裝欄位:

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>>>)。

不過,這項對應關係有例外狀況。如果 T 是 IBinder 或 AIDL 介面,@nullable 對於 Java、CPP 和 NDK 後端來說是無運算。舉例來說,在 CPP 後端,@nullable IBinder 和 IBinder 都同樣對應至 android::sp<IBinder>,由於這是強指標,因此已可為空值 (CPP 和 NDK 讀取作業仍會強制執行是否可為空值,但型別仍為 android::sp<IBinder> 或 ndk::SpAIBinder)。

在 Rust 後端,未實作 Default 的型別 (IBinder、AIDL 介面、ParcelFileDescriptor 和非結構化可打包物件) 會根據其內容對應至 Option<T>:

  • 方法回傳型別、in 參數和 inout 參數:只有在註解為 @nullable 時,這些型別才能為空值,對應至 Option<T> (或 in 參數的 Option<&T>,以及 inout 參數的 &mut Option<T>)。如果沒有 @nullable,則會直接對應至 T (或 &T 和 &mut T)。
  • 可封送欄位和 out 參數:由於產生的 Rust 可封送項目會衍生 Default,且 out 參數會預設初始化,因此即使沒有 @nullable,這些型別一律會對應至 Option<T> (而這些環境中的固定大小陣列會對應至 [Option<T>; N],out 參數中的動態陣列則會對應至 Vec<Option<T>>)。如果沒有 @nullable 註解,系統仍會在封裝期間強制執行不可為空值 (如果值為 None,則會傳回 StatusCode::UNEXPECTED_NULL)。

從 Android 13 開始,@nullable(heap=true) 可用於可封送欄位,以模擬遞迴型別。@nullable(heap=true) 無法與方法參數或回傳型別搭配使用。如果使用此註解,欄位會對應至 CPP 和 NDK 後端中堆積分配的參照 std::unique_ptr<T>。在 Java 後端中,@nullable(heap=true) 是無運算。

utf8InCpp

utf8InCpp 宣告 String 是以 UTF8 格式表示,適用於 CPP 後端。如名稱所示,註解對於其他後端是無運算。具體來說,在 Java 後端,String 一律為 UTF16,在 NDK 後端則為 UTF8。

只要可以使用 String 型別,即可附加這項註解,包括回傳值、參數、常數宣告和可封送欄位。

在 CPP 後端,AIDL 中的 @utf8InCpp String 會對應至 std::string,其中沒有註解的 String 會對應至使用 UTF16 的 android::String16。

VintfStability

VintfStability 宣告使用者定義型別 (介面、可封送和列舉) 可在系統和供應商網域中使用。如要進一步瞭解系統與供應商的互通性,請參閱「AIDL for HALs」。

註解不會變更型別的簽章,但設定後,型別的例項會標示為穩定,因此可以在供應商和系統程序之間傳輸。

註解只能附加至使用者定義的型別宣告,如下所示:

@VintfStability
interface IFoo {
    ....
}

@VintfStability
parcelable Data {
    ....
}

@VintfStability
enum Type {
    ....
}

如果類型標有 VintfStability,則類型中參照的任何其他類型也應標註為此類型。在以下範例中,Data 和 IBar 都應使用 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 註解會指定 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 會將可封送的宣告 (而非定義) 標示為穩定,以便從其他穩定的 AIDL 型別參照。這與 JavaOnlyStableParcelable 類似,但 NdkOnlyStableParcelable 會將可封送的宣告標示為 NDK 後端的穩定狀態,而非 Java。

如要使用這個可封裝物件,請按照下列步驟操作:

  • 您必須指定 ndk_header。
  • 您必須擁有指定可打包的 NDK 程式庫,且該程式庫必須編譯至程式庫中。舉例來說,在 cc_* 模組的核心建構系統中,請使用 static_libs 或 shared_libs。如果是 aidl_interface,請在 Android.bp 的 additional_shared_libraries 下方新增程式庫。

JavaOnlyStableParcelable

JavaOnlyStableParcelable 會將可打包的宣告 (而非定義) 標示為穩定,以便從其他穩定的 AIDL 型別參照。

穩定版 AIDL 要求所有使用者定義型別都必須穩定。如果是可打包物件,穩定性要求其欄位必須在 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 Data; // Data is NOT a structured parcelable

parcelable AnotherData {
    Data d; // Error
}

當您參照的可打包物件安全地做為 Android SDK 的一部分提供時,JavaOnlyStableParcelable 可讓您覆寫檢查:

@JavaOnlyStableParcelable
parcelable Data;

parcelable AnotherData {
    Data d; // OK
}

JavaDerive

JavaDerive 會在 Java 後端自動產生可封裝型別的方法:

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

註解需要額外參數,才能控制要生成哪些內容。支援的參數如下:

  • equals=true 會產生 equals 和 hashCode 方法。
  • toString=true 會產生 toString 方法,用於列印型別和欄位的名稱,例如 Data{number: 42, str: foo}。

JavaDefault (已淘汰)

Android 13 新增的 JavaDefault 可控制是否產生預設實作版本管理支援 (適用於 setDefaultImpl)。為節省空間,系統不再預設產生這項支援。

JavaPassthrough

JavaPassthrough 可讓產生的 Java API 加上任意 Java 註解。

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 後端,這個註解是無運算。

RustDerive

RustDerive 會自動為產生的 Rust 型別實作特徵。

註解需要額外參數,才能控制要生成哪些內容。支援的參數如下:

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

如要瞭解這些特徵,請參閱 Rust 說明文件。

FixedSize

FixedSize 會將結構化可封送物件標示為固定大小。標記後,您就無法在可打包物件中新增欄位。可打包物件的所有欄位都必須是固定大小的型別,包括原始型別、列舉、固定大小的陣列,以及標示 FixedSize 的其他可打包物件。

FixedSize 物件在 ndk 後端具有穩定的大小和對齊方式。

類型 大小 (位元組) 對齊 (位元組)
boolean 1 1
byte 1 1
char 2 2
int 4 4
long 8 8
float 4 4
double 8 8
parcelable 所有欄位的大小總計 所有欄位中最大的對齊方式
union 所有欄位中最大的大小 所有欄位中最大的對齊方式
enum 備份類型大小 支援類型對齊
T[N] (固定大小的陣列) T * N 的大小 「T」的對齊方式
String, IBinder, FileDescriptor, ParcelFileDescriptor N/A N/A

描述元

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 程式碼。