Język AIDL

Język AIDL jest luźno oparty na języku Java. Pliki określają umowę interfejsu oraz różne typy danych i stałe używane w tej umowie.

Pakiet

Każdy plik AIDL zaczyna się od opcjonalnego pakietu, który odpowiada nazwom pakietów w różnych backendach. Deklaracja pakietu wygląda tak:

    package my.package;

Podobnie jak w przypadku języka Java, pliki AIDL muszą znajdować się w strukturze folderów zgodnej z ich pakietem. Pliki z pakietem my.package muszą znajdować się w folderze my/package/.

Typy

W plikach AIDL jest wiele miejsc, w których można określić typy. Dokładną listę typów obsługiwanych w języku AIDL znajdziesz w artykule Typy backendów AIDL.

Adnotacje

Adnotacje są obsługiwane w kilku częściach języka AIDL. Listę adnotacji i miejsc, w których można ich używać, znajdziesz w artykule Adnotacje AIDL.

Import

Aby używać typów zdefiniowanych w innych interfejsach, musisz najpierw dodać zależności w systemie kompilacji. W modułach cc_*java_* Soong, w których pliki .aidl są używane bezpośrednio w srcs w kompilacjach platformy Android, możesz dodawać katalogi za pomocą pola aidl: { include_dirs: ... }. Informacje o importowaniu za pomocą aidl_interface znajdziesz tutaj.

Import wygląda tak:

    import some.package.Foo;  // explicit import

Podczas importowania typu w tym samym pakiecie można pominąć pakiet. Pominięcie pakietu może jednak prowadzić do niejednoznacznych błędów importu, gdy typy są określone bez pakietu i umieszczone w globalnej przestrzeni nazw (zwykle wszystkie typy powinny mieć przestrzeń nazw):

    import Foo;  // same as my.package.Foo

Definiowanie typów

Pliki AIDL zwykle definiują typy, które są używane jako interfejs.

Interfejsy

Oto przykład interfejsu AIDL:

    interface ITeleport {
        // Location defined elsewhere
        void teleport(Location baz, float speed);
        String getName();

        // ITeleportCallback defined elsewhere
        void methodWithCallback(ITeleportCallback callback);

        // ITeleportSession defined elsewhere
        ITeleportSession getASubInterface();
    }

Interfejs definiuje obiekt z serią metod. Metody mogą być asynchroniczne oneway (oneway void doFoo()) lub synchroniczne. Jeśli interfejs jest zdefiniowany jako oneway (oneway interface ITeleport {...}), wszystkie metody w nim są domyślnie oneway. Metody jednokierunkowe są wysyłane asynchronicznie i nie mogą zwracać wyniku. Metody jednokierunkowe z tego samego wątku do tego samego bindera również są wykonywane szeregowo (choć potencjalnie w różnych wątkach). Więcej informacji o konfigurowaniu wątków znajdziesz w artykule Zarządzanie wątkami w backendach AIDL.

Binder umożliwia udostępnianie wielu interfejsów i obiektów Binder za pomocą interfejsów Binder. Interfejsy AIDL często wykorzystują wywołania zwrotne w ramach wywołań metod, np. w przypadku ITeleportCallback w poprzednim przykładzie. Obiekty wywołania zwrotnego można ponownie wykorzystywać w przypadku wywołań tej samej lub różnych metod. Innym powszechnym zastosowaniem typów interfejsów jest zwracanie z metod podinterfejsów lub obiektów sesji, tak jak w przypadku ITeleportSession w poprzednim przykładzie. Zagnieżdżanie umożliwia hermetyzację różnych interfejsów API na poziomie interfejsu API lub na podstawie stanu środowiska wykonawczego. Sesja może na przykład oznaczać własność określonego zasobu. Gdy interfejsy są przekazywane wielokrotnie lub zwracane do klienta lub serwera, z którego pochodzą, zawsze zachowują równość wskaźników bazowego obiektu bindera.

Metody mogą mieć zero lub więcej argumentów. Argumenty metod mogą być typu in, out lub inout. Więcej informacji o tym, jak wpływa to na typy argumentów, znajdziesz w sekcji Kierunkowość backendów AIDL.

Parcelables

Opis tworzenia obiektów Parcelable specyficznych dla backendu znajdziesz w artykule Niestandardowe obiekty Parcelable backendów AIDL.

Android 10 i nowsze wersje obsługują definicje obiektów z interfejsem Parcelable bezpośrednio w AIDL. Ten typ obiektu Parcelable jest nazywany obiektem Parcelable o strukturze. Więcej informacji o tym, jak powiązane są ze sobą strukturalny i stabilny AIDL w kompilatorze AIDL i naszym systemie kompilacji, znajdziesz w artykule Strukturalny a stabilny AIDL.

Na przykład:

    package my.package;

    import my.package.Boo;

    parcelable Baz {
        @utf8InCpp String name = "baz";
        Boo boo;
    }

Związki zawodowe

Android 12 i nowsze wersje obsługują deklaracje typu tagged union. Na przykład:

    package my.package;

    import my.package.FooSettings;
    import my.package.BarSettings;

    union Settings {
        FooSettings fooSettings;
        BarSettings barSettings;
        @utf8InCpp String str;
        int number;
    }

Szczegółowe informacje o backendach znajdziesz w artykule AIDL Backends Unions.

Wykazy

Android 11 i nowsze wersje obsługują deklaracje wyliczeniowe. Na przykład:

    package my.package;

    enum Boo {
        A = 1 * 4,
        B = 3,
    }

Deklaracje typów zagnieżdżonych

Android 13 i nowsze wersje obsługują zagnieżdżone deklaracje typów. Na przykład:

    package my.package;

    import my.package.Baz;

    interface IFoo {
        void doFoo(Baz.Nested nested);  // defined in my/package/Baz.aidl
        void doBar(Bar bar);            // defined below

        parcelable Bar { ... }          // nested type definition
    }

Stałe

Niestandardowe interfejsy AIDL, obiekty parcelable i struktury union mogą też zawierać stałe liczbowe i ciągowe, np.:

    const @utf8InCpp String HAPPY = ":)";
    const String SAD = ":(";
    const byte BYTE_ME = 1;
    const int ANSWER = 6 * 7;

Wyrażenia stałe

Stałe AIDL, rozmiary tablic i enumeratory można określać za pomocą wyrażeń stałych. W wyrażeniach można używać nawiasów do zagnieżdżania operacji. Wartości wyrażeń stałych można stosować z wartościami całkowitymi lub zmiennoprzecinkowymi.

Literały truefalse reprezentują wartości logiczne. Wartości z ., ale bez sufiksu, np. 3.8, są traktowane jako wartości podwójne. Wartości zmiennoprzecinkowe mają sufiks f, np. 2.4f. Wartość całkowita z sufiksem l lub L oznacza 64-bitową wartość długą. W przeciwnym razie wartości całkowite otrzymują najmniejszy typ ze znakiem zachowujący wartość spośród 8-bitowego (bajt), 32-bitowego (int) i 64-bitowego (long). Wartość 256 jest uznawana za int, ale 255 + 1 przekracza zakres i staje się byte 0. Wartości szesnastkowe, np. 0x3, są najpierw interpretowane jako najmniejszy typ bez znaku zachowujący wartość między 32-bitowym a 64-bitowym, a następnie ponownie interpretowane jako wartości bez znaku. W związku z tym 0xffffffff ma wartość int-1. Od Androida 13 do stałych, np. 3u8, można dodawać sufiks u8, aby reprezentować wartość byte. Ten sufiks jest ważny, aby obliczenie, takie jak 0xffu8 * 3, było interpretowane jako -3 z typem byte, a 0xff * 3 jako 765 z typem int.

Obsługiwane operatory mają semantykę C++ i Javy. Operatory binarne są uporządkowane od najniższego do najwyższego priorytetu:|| && | ^ & == != < > <= >= << >> + - * / %. Operatory jednoargumentowe to + - ! ~.