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 會宣告系統可能不會提供已加註實體的值。

這個註解只能附加至方法傳回類型、方法參數和 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>

如果是類似清單的類型 L (例如 T[]List<T>),@nullable L會對應至 std::optional<std::vector<std::optional<T>>> (或 std::unique_ptr<std::vector<std::unique_ptr<T>>> (適用於 Android 11 以下版本的 CPP 後端)。

這個對應有例外狀況。當 TIBinder 或 AIDL 介面時,@nullable 是不人工管理。換句話說,@nullable IBinderIBinder 都等於 android::sp<IBinder>,因為這是有強的指標 (CPP 讀取仍會強制執行是否可為空值,但類型仍為 android::sp<IBinder>)。

從 Android 13 開始,@nullable(heap=true) 可用於 Parcelable 欄位以建立遞迴類型。@nullable(heap=true) 無法與方法參數或傳回類型搭配使用。加上該欄位註解時,該欄位會對應至 CPP/NDK 後端中的堆積分配參照 std::unique_ptr<T>@nullable(heap=true) 是 Java 後端中的免人工管理。

utf8InCpp

utf8InCpp 會在 CPP 後端宣告 String 以 UTF8 格式表示。顧名思義,註解為其他後端的免人工管理。具體來說,Java 後端中的 String 一律為 UTF16,NDK 後端中的 UTF8 則一律為 UTF8。

此註解可以附加至任何可以使用 String 類型的位置,包括回傳值、參數、常數宣告和 parcelable 欄位。

在 CPP 後端中,AIDL 中的 @utf8InCpp String 對應至 std::string,不含註解的 String 則對應至使用 UTF16 的 android::String16

請注意,utf8InCpp 註解的存在並不會改變字串透過線路傳輸的方式。所有字串一律會以 UTF16 線路傳輸。utf8InCpp 註解字串會先轉換為 UTF16,然後再傳輸。收到字串後,如果註解為 utf8InCpp,則會從 UTF16 轉換為 UTF8。

陷阱

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 註解代表有註解的 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,
}

如果省略註解,系統會假設 typebyte,後者會對應至 CPP 後端的 int8_t

type 引數只能設為下列完整性類型:

  • byte (寬 8 位元)
  • int (寬 32 位元)
  • long (寬 64 位元)

NdkOnlyStableParcelable

NdkOnlyStableParcelable 會將可包裝宣告 (非定義) 標示為穩定版,以便可從其他穩定版 AIDL 類型參照該宣告。這與 JavaOnlyStableParcelable 類似,但 NdkOnlyStableParcelable 會將 parcelable 宣告標示為 NDK 後端的穩定版,而非 Java。

如何使用這個 parcelable:

  • 您必須指定 ndk_header
  • 您必須使用指定 parcelable 的 NDK 程式庫,且程式庫必須編譯到程式庫中。舉例來說,在 cc_* 模組的核心建構系統中,請使用 static_libsshared_libs。若是 aidl_interface,請在 Android.bpadditional_shared_libraries 底下新增程式庫。

JavaOnlyStableParcelable

JavaOnlyStableParcelable 會將可包裝宣告 (非定義) 標示為穩定版,以便可從其他穩定版 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
}

JavaOnlyStableParcelable 可讓您在 Android SDK 已安全取得所參照的 Parcel 時覆寫檢查。

@JavaOnlyStableParcelable
parcelable Data;

parcelable AnotherData {
    Data d; // OK
}

JavaDerive

JavaDerive 會自動在 Java 後端中產生可剖析類型的方法。

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

註解需要其他參數來控制要產生的內容。支援的參數如下:

  • equals=true 會產生 equalshashCode 方法。
  • toString=true 會產生 toString 方法,用於顯示類型和欄位的名稱。例如:Data{number: 42, str: foo}

JavaDefault (JavaDefault)

Android 13 新增了 JavaDefault,可控管是否要為 setDefaultImpl 產生預設的實作版本管理支援。為節省空間,系統預設不會再產生這項支援功能。

Java 直通式

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

以下 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 後端的免人工管理元件。

固定大小

FixedSize 會將結構化 Parcel 標示為固定大小。標記之後, parcelable 無法加入新的欄位。parcelable 的所有欄位也必須設為固定大小的類型,包括原始類型、列舉、固定大小陣列,以及其他加上 FixedSize 的 parcelable。

這不保證在不同位元性之間會獲得任何保證,因此不建議用於混合型通訊。

描述元

Descriptor 強制規定可指定介面的介面描述元。

package android.foo;

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

這個介面的描述元為 android.bar.IWorld。如果缺少 Descriptor 註解,描述元為 android.foo.IHello

如要重新命名已經發布的介面,這項功能就能派上用場。在重新命名前,如果將已重新命名的介面描述元與介面描述元相同,即可讓這兩個介面彼此通訊。

在留言中使用 @隱藏

AIDL 編譯器可在註解中辨識 @hide,並傳遞至 Java 輸出內容,供 Metalava 取貨。這項註解可確保 Android 建構系統知道 AIDL API 並非 SDK API。

@已在註解中淘汰

AIDL 編譯器會將註解中的 @deprecated 視為標記,用於識別不再使用的 AIDL 實體。

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

每個後端都會使用後端專屬註解或屬性來標示已淘汰的實體,這樣一來,如果用戶端程式碼參照已淘汰的實體,就會發出警告。舉例來說,@Deprecated 註解和 @deprecated 標記會附加至 Java 產生的程式碼。