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.

Przesyłka

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 plików Java, pliki AIDL muszą znajdować się w strukturze folderów odpowiadającej ich pakietowi. Pliki z pakietem my.package muszą znajdować się w folderze my/package/.

Typy

W plikach AIDL typy można określać w wielu miejscach. Dokładną listę typów obsługiwanych w języku AIDL znajdziesz w artykule Typy backendów AIDL.

Adnotacje

kilka części adnotacji dotyczących obsługi języka w AIDL; Listę adnotacji i miejsca, w których można je stosować, znajdziesz w artykule Adnotacje AIDL.

Importy

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

Import wygląda tak:

    import some.package.Foo;  // explicit import

Podczas importowania typu w tym samym pakiecie można pominąć ten pakiet. Pominięcie pakietu może jednak spowodować niejednoznaczne błędy importowania, gdy typy są określone bez pakietu i znajdują się w przestrzeni nazw globalnej (zazwyczaj wszystkie typy powinny mieć przestrzeń nazw):

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

Definiowanie typów

Pliki AIDL zwykle definiują typy 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 za pomocą serii metod. Metody mogą być asynchroniczne (oneway) lub synchroniczne.oneway void doFoo() 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. Jednokierunkowe metody z tego samego wątku do tego samego łącznika są również wykonywane sekwencyjnie (chociaż potencjalnie w różnych wątkach). Informacje o konfigurowaniu wątków znajdziesz w artykule Zarządzanie wątkami w systemie AIDL backend.

Binder umożliwia udostępnianie wielu interfejsów i obiektów bindera za pomocą interfejsów bindera. Interfejsy AIDL często używają wywołań zwrotnych w ramach wywołań metod, na przykład ITeleportCallback w poprzednim przykładzie. Możesz używać wywołań zwrotnych w ramach wywołań tej samej metody lub różnych metod. Innym częstym zastosowaniem typów interfejsów jest tworzenie podrzędnych interfejsów lub obiektów sesji zwracanych przez metody takie jak ITeleportSession w poprzednim przykładzie. Dzięki temu różne interfejsy API mogą być opakowane na poziomie interfejsu API lub na podstawie stanu w czasie wykonywania. Sesja może na przykład reprezentować 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źnika do podstawowego obiektu binder.

Metody mogą mieć zero lub więcej argumentów. Argumenty metod mogą być typu in, out lub inout. Aby dowiedzieć się, jak to wpływa na typy argumentów, zapoznaj się z artykułem AIDL: kierunkowość backendów.

Parcelables

Aby dowiedzieć się, jak tworzyć obiekty parcelable dla konkretnego backendu, zapoznaj się z artykułem Parcelable dla backendu AIDL.

Android 10 i nowsze wersje obsługują definicje parcelable bezpośrednio w AIDL. Ten typ obiektu do zapakowania nazywamy obiektem uporządkowanym. Więcej informacji o związku między skompilowanymi i stabilnymi plikami AIDL w kompilatorze AIDL i naszym systemie kompilacji znajdziesz w artykule Kompilowane i stabilne pliki AIDL.

Przykład:

    package my.package;

    import my.package.Boo;

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

Związki

Android 12 i nowsze wersje obsługują deklaracje z oznaczonymi typami z unii. 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 związkach backendów AIDL.

Wykazy

Android 11 i nowsze obsługują deklaracje typów wyliczeniowych. Przykład:

    package my.package;

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

Zagnieżdżone deklaracje typu

Android 13 i nowsze wersje obsługują zagnieżdżone deklaracje typów. 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 typy zjednoczone mogą też zawierać stałe liczbowe i ciągowe, takie jak:

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

wyrażenia statyczne,

Stałej AIDL, rozmiarów tablic i enumeracji można używać za pomocą wyrażeń stałych. W wyrażeniach można używać nawiasów do zagnieżdżania operacji. Wartości wyrażenia stałego mogą być używane z wartościami całkowitymi lub zmiennoprzecinkowymi.

Literaly truefalse reprezentują wartości logiczne. Wartości z ., ale bez przyrostka, np. 3.8, są uznawane za wartości podwójne. Wartości typu float mają sufiks f, np. 2.4f. Wartość całkowita z sufiksem l lub L oznacza 64-bitową wartość. W przeciwnym razie wartości całkowite otrzymują najmniejszy typ ze znakiem, który zachowuje wartość, spośród 8-bitowego (byte), 32-bitowego (int) i 64-bitowego (long). Dlatego 256 jest uważane za int, ale 255 + 1 przepełnia się, aby być byte 0. Wartości szesnastkowe, takie jak 0x3, są najpierw interpretowane jako najmniejszy typ bez znaku, który zachowuje wartość, między 32- i 64-bitowym, a następnie ponownie interpretowane jako wartości bez znaku. W związku z tym 0xffffffff ma wartość int -1. Począwszy od Androida 13, do stałych wartości, takich jak 3u8, można dodawać sufiks u8, aby reprezentowały one wartość byte. Ten przyrostek jest ważny, ponieważ dzięki niemu obliczenie takie jak 0xffu8 * 3 jest interpretowane jako -3 typu byte, podczas gdy 0xff * 3 jest 765 typu int.

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