Adnotacje w AIDL

AIDL obsługuje adnotacje, które dostarczają kompilatorowi AIDL dodatkowe informacje o elemencie z adnotacjami, co również ma wpływ na wygenerowany kod pośredni.

Składnia jest podobna do składni w Javie:

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

AnnotationName to nazwa adnotacji, a AidlEntity to encja AIDL, np. interface Foo, void method() lub int arg. Do encji, która następuje po nim, jest dołączona adnotacja.

Niektóre adnotacje mogą mieć argumenty w nawiasach, jak pokazano powyżej. Adnotacje, które nie mają argumentu, nie wymagają nawiasów. Na przykład:

@AnnotationName AidlEntity

Te adnotacje różnią się od adnotacji w języku Java, choć wyglądają bardzo podobnie. Użytkownicy nie mogą definiować własnych adnotacji AIDL. Wszystkie adnotacje są zdefiniowane wstępnie. Niektóre adnotacje mają zastosowanie tylko do określonego backendu i nie działają w innych backendach. Obowiązują różne ograniczenia, do których można je dołączyć.

Oto lista wstępnie zdefiniowanych adnotacji AIDL:

Adnotacje Dodano w wersji na Androida
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 oznacza, że wartość opatrzonego adnotacją elementu może nie być podana.

Tę adnotację można dołączyć tylko do typów zwracanych przez metodę, parametrów metody i pól możliwych do podzielenia.

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

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

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

Adnotacji nie można dołączać do typów podstawowych. Poniżej znajduje się błąd.

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

Ta adnotacja nie ma zastosowania w przypadku zaplecza w języku Java. Dzieje się tak, ponieważ w języku Java wszystkie typy inne niż prymitywne są przekazywane przez odniesienie, które może być null.

W backendzie CPP identyfikator @nullable T jest mapowany na std::unique_ptr<T> w Androidzie 11 lub starszym oraz na std::optional<T> w Androidzie 12 lub nowszym.

W backendzie NDK wartość @nullable T zawsze jest mapowana na std::optional<T>.

W backendzie Rust @nullable T zawsze mapuje się na Option<T>.

W przypadku typu L, np. T[] lub List<T>, @nullable L jest mapowane na std::optional<std::vector<std::optional<T>>> (lub std::unique_ptr<std::vector<std::unique_ptr<T>>> w przypadku backendu CPP w Androidzie 11 lub niższym).

W przypadku tego mapowania istnieje wyjątek. Gdy T jest IBinder lub interfejsem AIDL, @nullable nie wykonuje żadnej operacji na żadnym backendzie oprócz Rust. Innymi słowy, zarówno @nullable IBinder, jak i IBinder mapują się na android::sp<IBinder>, która jest już typu nullable, ponieważ jest to silny wskaźnik (w przypadku odczytu w C++ nadal obowiązuje zasada nullable, ale typ jest nadal android::sp<IBinder>). W Rust te typy są nullable tylko wtedy, gdy są opatrzone adnotacją @nullable. Jeśli są opatrzone adnotacjami, są przypisane do Option<T>.

Począwszy od Androida 13 możesz używać pola @nullable(heap=true) do modelowania typów rekurencyjnych w przypadku pól parcelowalnych. @nullable(heap=true) nie może być używana z parametrami metody ani typami zwracanymi. Gdy jest ono opatrzone adnotacjami, jest mapowane na odwołanie std::unique_ptr<T> przydzielone na stosie w backendach CPP/NDK. Interfejs @nullable(heap=true) nie działa w backendzie Java.

utf8InCpp

utf8InCpp deklaruje, że String jest reprezentowany w formacie UTF-8 na zapleczu CPP. Jak sama nazwa wskazuje, adnotacja nie jest operacją w innych backendach. W szczególności String jest zawsze w formacie UTF-16 w backendzie Java i w formacie UTF-8 w backendzie NDK.

Tę adnotację można dołączyć wszędzie tam, gdzie można użyć typu String, w tym w wartościach zwracanych, parametrach, stałych deklaracjach i polach parcelable.

W przypadku backendu w C++ parametr @utf8InCpp String w pliku AIDL jest mapowany na std::string, natomiast parametr String bez adnotacji jest mapowany na android::String16, gdzie używany jest format UTF-16.

Pamiętaj, że obecność adnotacji utf8InCpp nie zmienia sposobu przesyłania ciągów znaków przez sieć. Ciągi znaków są zawsze przesyłane w formacie UTF16. Przed przesłaniem ciąg znaków z adnotacjami utf8InCpp jest konwertowany na UTF16. Gdy otrzymany ciąg znaków został oznaczony jako utf8InCpp, jest on konwertowany z UTF16 na UTF8.

VintfStability

VintfStability deklaruje, że typ zdefiniowany przez użytkownika (interface, parcelable, enum) może być używany w domenie systemu i dostawcy. Więcej informacji o interoperacyjności dostawców systemów znajdziesz w AIDL dla HAL-i.

Adnotacja nie zmienia sygnatury typu, ale po jej ustawieniu instancja typu jest oznaczana jako stabilna, aby mogła być przekazywana przez procesy dostawcy i systemu.

Adnotację można dołączyć tylko do deklaracji typu zdefiniowanego przez użytkownika, jak pokazano tutaj:

@VintfStability
interface IFoo {
    ....
}

@VintfStability
parcelable Data {
    ....
}

@VintfStability
enum Type {
    ....
}

Gdy typ jest opatrzony adnotacją VintfStability, powinien być również opatrzony adnotacją do każdego innego typu, do którego odwołuje się typ. W tym przykładzie DataIBar powinny być oznaczone jako 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 {...}

Oprócz tego pliki AIDL definiujące typy z adnotacjami VintfStability można tworzyć tylko za pomocą typu modułu aidl_interface Soong z właściwością stability ustawioną na "vintf".

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

UnsupportedAppUsage

Adnotacja UnsupportedAppUsage oznacza, że oznaczony typ AIDL jest częścią interfejsu spoza pakietu SDK, który był dostępny dla starszych aplikacji. Więcej informacji o ukrytych interfejsach API znajdziesz w artykule Ograniczenia dotyczące interfejsów API innych niż szkielety aplikacji.

Adnotacja UnsupportedAppUsage nie ma wpływu na działanie wygenerowanego kodu. Adnotacja dotyczy tylko wygenerowanej klasy Java i ma taką samą nazwę.

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

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

To ustawienie nie jest dostępne dla backendów innych niż Java.

Podparcie

Adnotacja Backing określa typ pamięci masowej dla typu enum AIDL.

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

W backendzie CPP generuje to klasę typu int32_t z enumeracją C++.

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

Jeśli pominiesz adnotację, zostanie domyślnie przyjęta wartość type, która w przypadku backendu CPP jest mapowana na wartość int8_t.byte

Argument type można ustawić tylko na te typy całek:

  • byte (8-bitowy)
  • int (32-bitowa szerokość)
  • long (64-bitowa szerokość)

NdkOnlyStableParcelable

NdkOnlyStableParcelable oznacza deklarację parcelable (nie definicję) jako stabilną, aby można było się do niej odwoływać z innych stabilnych typów AIDL. Jest to podobne do JavaOnlyStableParcelable, ale NdkOnlyStableParcelable oznacza deklarację parcelable jako stabilną dla backendu NDK, a nie dla Javy.

Aby użyć tej funkcji:

  • Musisz podać wartość ndk_header.
  • Musisz mieć bibliotekę NDK, która określa parcelable, i musisz ją skompilować w bibliotece. Na przykład w podstawowym systemie kompilacji modułu cc_* użyj static_libs lub shared_libs. W przypadku aidl_interface dodaj bibliotekę w sekcji additional_shared_libraries w Android.bp.

JavaOnlyStableParcelable,

JavaOnlyStableParcelable oznacza deklarację parcelable (nie definicję) jako stabilną, aby można było się do niej odwoływać z innych stabilnych typów AIDL.

Stabilny interfejs AIDL wymaga, aby wszystkie typy zdefiniowane przez użytkownika były stabilne. Stabilność w przypadku pakietów SDK wymaga jawnego opisania pól w pliku źródłowym 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
}

Jeśli obiekt Parcelable był nieustrukturyzowany (lub tylko zadeklarowany), nie można go odwoływać.

parcelable Data; // Data is NOT a structured parcelable

parcelable AnotherData {
    Data d; // Error
}

JavaOnlyStableParcelable pozwala zastąpić sprawdzenie, gdy referencje do obiektu Parcelable są już bezpiecznie dostępne w pakiecie SDK Androida.

@JavaOnlyStableParcelable
parcelable Data;

parcelable AnotherData {
    Data d; // OK
}

JavaDerive

JavaDerive automatycznie generuje w backendzie Java metody dla typów papierowych.

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

Adnotacja wymaga dodatkowych parametrów określających, co ma być generowane. Obsługiwane parametry:

  • equals=true generuje metody equalshashCode.
  • toString=true generuje metodę toString, która wypisuje nazwę typu i pola. Na przykład: Data{number: 42, str: foo}

JavaDefault

JavaDefault, dodana w Androidzie 13, określa, czy domyślna obsługa wersji implementacji (w przypadku setDefaultImpl) jest generowana. Domyślnie nie jest już generowana, aby oszczędzać miejsce.

JavaPassthrough

JavaPassthrough umożliwia dodanie do wygenerowanego interfejsu Java API adnotacji przy użyciu dowolnej adnotacji w języku Java.

te adnotacje w pliku AIDL:

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

stać się

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

w wygenerowanym kodzie Java.

Wartość parametru annotation jest emitowana bezpośrednio. Kompilator AIDL nie sprawdza wartości parametru. Jeśli wystąpi jakikolwiek błąd składni na poziomie Javy, nie zostanie on wykryty przez kompilator AIDL, ale przez kompilator Javy.

Tę adnotację można dołączyć do dowolnej jednostki AIDL. Ta adnotacja jest ignorowana w przypadku backendów innych niż Java.

Stały rozmiar

FixedSize oznacza działkę strukturalną jako stały rozmiar. Po oznaczeniu nie można dodawać do niego nowych pól. Wszystkie pola obiektu parcelable muszą być typu o stałym rozmiarze, w tym typów prymitywnych, enumeracji, tablic o stałym rozmiarze i innych obiektów parcelable oznaczonych FixedSize.

Nie zapewnia to żadnej gwarancji w przypadku różnych głębokości bitów i nie należy polegać na tym w przypadku komunikacji z różną głębią bitów.

Deskryptor

Descriptor nakazuje określenie interfejsu w interfejsie.

package android.foo;

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

Deskryptor tego interfejsu to android.bar.IWorld. Jeśli brak adnotacji Descriptor, opis będzie wyglądał tak: android.foo.IHello.

Jest to przydatne, gdy chcesz zmienić nazwę już opublikowanego interfejsu. Ustawienie identycznego deskryptora przemianowanego interfejsu co deskryptora interfejsu przed przemianą umożliwia wzajemną komunikację obu interfejsów.

@hide w komentarzach

Kompilator AIDL rozpoznaje w komentarzach wartość @hide i przekazuje ją do danych wyjściowych Javy, aby umożliwić ich odbiór. Dzięki temu komentarzowi system kompilacji Androida wie, że interfejsy AIDL API nie są interfejsami SDK API.

@deprecated w komentarzach

Kompilator AIDL rozpoznaje @deprecated w komentarzach jako tag identyfikujący element AIDL, którego nie należy już używać.

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

Każdy backend oznacza wycofane elementy za pomocą adnotacji lub atrybutu specyficznego dla backendu, aby w przypadku odwołania do wycofanych elementów kod klienta otrzymał ostrzeżenie. Na przykład do wygenerowanego kodu Java dołączone są adnotacja @Deprecated i tag @deprecated.