Chú thích trong AIDL

AIDL hỗ trợ các chú thích cung cấp cho trình biên dịch AIDL thông tin bổ sung về phần tử được chú thích, điều này cũng ảnh hưởng đến mã gốc được tạo.

Cú pháp tương tự như cú pháp của Java:

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

Trong đó, AnnotationName là tên của chú thích và AidlEntity là một thực thể AIDL như interface Foo, void method() hoặc int arg. Chú thích được đính kèm vào thực thể theo sau chú thích đó.

Một số chú thích có thể có các đối số được đặt bên trong dấu ngoặc đơn, như minh hoạ ở trên. Chú thích không có đối số thì không cần dấu ngoặc đơn. Ví dụ:

@AnnotationName AidlEntity

Các chú thích này không giống với chú thích Java, mặc dù chúng trông rất giống nhau. Người dùng không thể xác định chú thích AIDL tuỳ chỉnh; tất cả chú thích đều được xác định trước. Một số chú thích chỉ ảnh hưởng đến một số phần phụ trợ nhất định và không hoạt động trong các phần phụ trợ khác. Chúng có các quy định hạn chế khác nhau về nơi có thể đính kèm.

Dưới đây là danh sách các chú thích AIDL được xác định trước:

Chú thích Được thêm vào phiên bản 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

có thể rỗng

nullable khai báo rằng bạn không thể cung cấp giá trị của thực thể được chú thích.

Bạn chỉ có thể đính kèm chú giải này vào các loại dữ liệu trả về của phương thức, tham số phương thức và các trường có thể chuyển đổi thành đối tượng Parcel.

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

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

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

Bạn không thể đính kèm chú giải vào các kiểu dữ liệu nguyên thuỷ. Đã xảy ra lỗi sau.

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

Chú thích này không có tác dụng đối với phụ trợ Java. Điều này là do trong Java, tất cả các kiểu không nguyên thuỷ đều được truyền theo tham chiếu, có thể là null.

Trong phần phụ trợ CPP, @nullable T sẽ ánh xạ đến std::unique_ptr<T> trong Android 11 trở xuống và đến std::optional<T> trong Android 12 trở lên.

Trong phần phụ trợ NDK, @nullable T luôn liên kết với std::optional<T>.

Trong phần phụ trợ Rust, @nullable T luôn ánh xạ đến Option<T>.

Đối với loại giống như danh sách L, chẳng hạn như T[] hoặc List<T>, @nullable L sẽ ánh xạ đến std::optional<std::vector<std::optional<T>>> (hoặc std::unique_ptr<std::vector<std::unique_ptr<T>>> trong trường hợp phụ trợ CPP cho Android 11 trở xuống).

Có một trường hợp ngoại lệ đối với mối liên kết này. Khi TIBinder hoặc một giao diện AIDL, @nullable sẽ không hoạt động đối với tất cả các phần phụ trợ, ngoại trừ Rust. Nói cách khác, cả @nullable IBinderIBinder đều ánh xạ đến android::sp<IBinder>, vốn đã có thể rỗng vì đây là một con trỏ mạnh (CPP vẫn thực thi tính chất rỗng, nhưng loại này vẫn là android::sp<IBinder>). Trong Rust, các loại này chỉ là nullable nếu được chú thích bằng @nullable. Chúng sẽ được liên kết với Option<T> nếu được chú thích.

Kể từ Android 13, @nullable(heap=true) có thể dùng cho các trường có thể chuyển đổi thành đối tượng để mô hình hoá các loại đệ quy. Không thể sử dụng @nullable(heap=true) với các tham số phương thức hoặc loại dữ liệu trả về. Khi được chú thích bằng trường này, trường sẽ được ánh xạ đến một tham chiếu std::unique_ptr<T> được phân bổ theo khối lượng lớn trong các phần phụ trợ CPP/NDK. @nullable(heap=true) là no-op trong phần phụ trợ Java.

utf8InCpp

utf8InCpp khai báo rằng String được biểu thị ở định dạng UTF8 cho phần phụ trợ CPP. Như tên của chú thích này cho thấy, chú thích này không có tác dụng đối với các phần phụ trợ khác. Cụ thể, String luôn là UTF16 trong phần phụ trợ Java và UTF8 trong phần phụ trợ NDK.

Bạn có thể đính kèm chú giải này ở bất kỳ vị trí nào có thể dùng loại String, bao gồm cả giá trị trả về, tham số, khai báo hằng số và các trường có thể phân chia.

Đối với phần phụ trợ CPP, @utf8InCpp String trong AIDL sẽ ánh xạ đến std::string, trong khi String không có chú giải sẽ ánh xạ đến android::String16 khi dùng UTF16.

Xin lưu ý rằng sự tồn tại của chú thích utf8InCpp không thay đổi cách truyền chuỗi qua mạng. Các chuỗi luôn được truyền dưới dạng UTF16 qua mạng. Một chuỗi được chú thích utf8InCpp sẽ được chuyển đổi thành UTF16 trước khi được truyền. Khi nhận được một chuỗi, chuỗi đó sẽ được chuyển đổi từ UTF16 sang UTF8 nếu được chú thích là utf8InCpp.

VintfStability

VintfStability khai báo rằng một loại do người dùng xác định (giao diện, có thể đóng gói và enum) có thể được dùng trên các miền hệ thống và nhà cung cấp. Hãy xem AIDL cho HAL để biết thêm về khả năng tương tác giữa hệ thống và nhà cung cấp.

Chú giải này không thay đổi chữ ký của kiểu, nhưng khi được đặt, thực thể của kiểu sẽ được đánh dấu là ổn định để có thể di chuyển qua các quy trình của nhà cung cấp và hệ thống.

Chú thích chỉ có thể được đính kèm vào các khai báo kiểu do người dùng xác định như minh hoạ ở đây:

@VintfStability
interface IFoo {
    ....
}

@VintfStability
parcelable Data {
    ....
}

@VintfStability
enum Type {
    ....
}

Khi một loại được chú thích bằng VintfStability, mọi loại khác được tham chiếu trong loại đó cũng phải được chú thích như vậy. Trong ví dụ sau, cả DataIBar đều phải được chú thích bằng 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 {...}

Ngoài ra, các tệp AIDL xác định các loại được chú thích bằng VintfStability chỉ có thể được tạo bằng loại mô-đun aidl_interface Soong, với thuộc tính stability được đặt thành "vintf".

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

UnsupportedAppUsage

Chú thích UnsupportedAppUsage cho biết loại AIDL được chú thích là một phần của giao diện không phải SDK mà các ứng dụng cũ có thể truy cập. Hãy xem bài viết Các hạn chế đối với giao diện không phải SDK để biết thêm thông tin về các API ẩn.

Chú thích UnsupportedAppUsage không ảnh hưởng đến hành vi của mã được tạo. Chú giải này chỉ chú giải lớp Java đã tạo bằng chú giải Java có cùng tên.

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

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

Đây là một thao tác không có hiệu lực đối với các phần phụ trợ không phải Java.

Lót

Chú thích Backing chỉ định loại bộ nhớ của một loại enum AIDL.

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

Trong phần phụ trợ CPP, thao tác này sẽ phát ra một lớp liệt kê C++ thuộc loại int32_t.

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

Nếu bạn bỏ qua chú thích này, thì type sẽ được giả định là byte, ánh xạ đến int8_t cho phần phụ trợ CPP.

Bạn chỉ có thể đặt đối số type thành các loại số nguyên sau:

  • byte (rộng 8 bit)
  • int (rộng 32 bit)
  • long (rộng 64 bit)

NdkOnlyStableParcelable

NdkOnlyStableParcelable đánh dấu một khai báo có thể chuyển đổi thành gói (không phải định nghĩa) là ổn định để có thể được tham chiếu từ các loại AIDL ổn định khác. Thao tác này tương tự như JavaOnlyStableParcelable, nhưng NdkOnlyStableParcelable đánh dấu một khai báo có thể phân chia là ổn định cho phần phụ trợ NDK thay vì cho Java.

Cách sử dụng đối tượng có thể chuyển đổi này:

  • Bạn phải chỉ định ndk_header.
  • Bạn phải có một thư viện NDK chỉ định đối tượng có thể chuyển đổi và thư viện đó phải được biên dịch thành thư viện. Ví dụ: trong hệ thống bản dựng cốt lõi trên mô-đun cc_*, hãy sử dụng static_libs hoặc shared_libs. Đối với aidl_interface, hãy thêm thư viện trong additional_shared_libraries vào Android.bp.

JavaOnlyStableParcelable

JavaOnlyStableParcelable đánh dấu một khai báo có thể chuyển đổi thành gói (không phải định nghĩa) là ổn định để có thể được tham chiếu từ các loại AIDL ổn định khác.

AIDL ổn định yêu cầu tất cả các loại do người dùng xác định đều phải ổn định. Đối với các đối tượng có thể đóng gói, để ổn định, bạn cần mô tả rõ ràng các trường của đối tượng đó trong tệp nguồn 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
}

Nếu đối tượng có thể chuyển đổi tuần tự không có cấu trúc (hoặc chỉ được khai báo), thì bạn không thể tham chiếu đối tượng đó.

parcelable Data; // Data is NOT a structured parcelable

parcelable AnotherData {
    Data d; // Error
}

JavaOnlyStableParcelable cho phép bạn ghi đè quy trình kiểm tra khi đối tượng có thể chuyển đổi thành gói mà bạn đang tham chiếu đã có sẵn một cách an toàn trong SDK Android.

@JavaOnlyStableParcelable
parcelable Data;

parcelable AnotherData {
    Data d; // OK
}

JavaDerive

JavaDerive tự động tạo các phương thức cho các loại có thể chuyển đổi thành gói trong phần phụ trợ Java.

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

Chú thích này yêu cầu các tham số bổ sung để kiểm soát nội dung cần tạo. Các tham số được hỗ trợ là:

  • equals=true tạo ra các phương thức equalshashCode.
  • toString=true tạo phương thức toString in tên của loại và các trường. Ví dụ: Data{number: 42, str: foo}

JavaDefault

JavaDefault (được thêm vào Android 13) kiểm soát việc có tạo tính năng hỗ trợ lập phiên bản triển khai mặc định hay không (đối với setDefaultImpl). Tính năng hỗ trợ này không còn được tạo theo mặc định để tiết kiệm dung lượng.

JavaPassthrough

JavaPassthrough cho phép chú thích API Java đã tạo bằng một chú thích Java tuỳ ý.

Các chú thích sau trong AIDL

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

trở thành

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

trong mã Java được tạo.

Giá trị của tham số annotation được phát trực tiếp. Trình biên dịch AIDL không xem xét giá trị của tham số. Nếu có bất kỳ lỗi cú pháp nào ở cấp Java, thì trình biên dịch AIDL sẽ không phát hiện được mà trình biên dịch Java sẽ phát hiện được.

Bạn có thể đính kèm chú thích này vào bất kỳ thực thể AIDL nào. Chú giải này không có tác dụng đối với các phần phụ trợ không phải Java.

RustDerive

RustDerive tự động triển khai các đặc điểm cho các loại Rust được tạo.

Chú thích này yêu cầu các tham số bổ sung để kiểm soát nội dung cần tạo. Các tham số được hỗ trợ là:

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

Để biết nội dung giải thích về các đặc điểm này, hãy xem https://doc.rust-lang.org.

FixedSize

FixedSize đánh dấu một đối tượng có thể chuyển đổi tuần tự có cấu trúc là kích thước cố định. Sau khi được đánh dấu, parcelable sẽ không được phép thêm các trường mới vào đó. Tất cả các trường của đối tượng có thể chuyển đổi thành gói cũng phải là các loại có kích thước cố định, bao gồm các loại nguyên thuỷ, enum, mảng có kích thước cố định và các đối tượng có thể chuyển đổi thành gói khác được đánh dấu bằng FixedSize.

Điều này không đảm bảo bất kỳ điều gì trên các độ dài bit khác nhau và không nên dựa vào đó để giao tiếp với độ dài bit hỗn hợp.

Nội dung mô tả

Descriptor buộc chỉ định bộ mô tả giao diện của một giao diện.

package android.foo;

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

Trình mô tả của giao diện này là android.bar.IWorld. Nếu chú thích Descriptor bị thiếu, thì phần mô tả sẽ là android.foo.IHello.

Điều này hữu ích khi đổi tên một giao diện đã được xuất bản. Việc đặt giá trị mô tả của giao diện được đổi tên giống với giá trị mô tả của giao diện trước khi đổi tên cho phép hai giao diện giao tiếp với nhau.

@hide trong phần bình luận

Trình biên dịch AIDL nhận dạng @hide trong phần bình luận và chuyển phần bình luận đó sang đầu ra Java để metalava nhận. Chú thích này đảm bảo rằng hệ thống tạo Android biết rằng các API AIDL không phải là API SDK.

@deprecated trong phần nhận xét

Trình biên dịch AIDL nhận dạng @deprecated trong phần nhận xét dưới dạng thẻ để xác định thực thể AIDL không còn được dùng nữa.

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

Mỗi phần phụ trợ đánh dấu các thực thể không dùng nữa bằng một chú thích hoặc thuộc tính dành riêng cho phần phụ trợ để mã ứng dụng được cảnh báo nếu mã này tham chiếu đến các thực thể không dùng nữa. Ví dụ: chú giải @Deprecated và thẻ @deprecated được đính kèm vào mã Java đã tạo.