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 odpowiadającej ich pakietowi. 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
Kilka części języka AIDL obsługuje adnotacje. Listę adnotacji i miejsc, 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_* i java_*, 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 importach 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. Pomijanie pakietu może jednak prowadzić do niejednoznacznych błędów importu, gdy typy są określane bez pakietu i umieszczane 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ć
oneway (oneway void doFoo()) lub synchroniczne. Jeśli interfejs jest zdefiniowany jako
oneway (oneway interface ITeleport {...}), wszystkie jego metody 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 są też wykonywane szeregowo (choć potencjalnie w różnych wątkach). Informacje 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 bindera za pomocą interfejsów bindera. Interfejsy AIDL często używają wywołań zwrotnych jako części wywołań metod, np. w przypadku ITeleportCallback w poprzednim przykładzie. Możesz ponownie używać obiektów wywołań zwrotnych między wywołaniami tej samej metody lub wywołaniami różnych metod. Innym częstym zastosowaniem typów interfejsów jest zwracanie podinterfejsów lub obiektów sesji z metod, np. w przypadku ITeleportSession w poprzednim przykładzie. To zagnieżdżenie umożliwia hermetyzację różnych interfejsów API na poziomie interfejsu API lub na podstawie stanu środowiska wykonawczego. 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źników bazowego obiektu bindera.
Metody mogą mieć 0 lub więcej argumentów. Argumenty metod mogą być
in, out lub inout. Informacje o tym, jak wpływa to na typy argumentów,
znajdziesz
w artykule Kierunkowość backendów AIDL.
Parcelable
Opis tworzenia parcelable specyficznych dla backendu znajdziesz w artykule Niestandardowe parcelable w backendach AIDL.
Android 10 i nowsze wersje obsługują definicje parcelable bezpośrednio w AIDL. Ten typ parcelable nazywa się parcelable strukturalnym. Więcej informacji o tym, jak strukturalne i stabilne AIDL są powiązane w kompilatorze AIDL i naszym systemie kompilacji, znajdziesz w artykule Strukturalne a stabilne AIDL.
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 oznaczonych związków zawodowych. 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 dotyczące backendu znajdziesz w artykule Związki zawodowe w backendach AIDL.
Wartości w polu enum
Android 11 i nowsze wersje obsługują deklaracje wartości w polu enum. 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ą deklaracje typów zagnieżdżonych. 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, parcelable i związki zawodowe 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 stałe
Stałe AIDL, rozmiary tablic i wyliczenia można określać za pomocą wyrażeń stałych. Wyrażenia mogą używać nawiasów do zagnieżdżania operacji. Wartości wyrażeń stałych można używać z wartościami całkowitymi lub zmiennoprzecinkowymi.
Literały true i false reprezentują wartości logiczne. Wartości z . ale
bez sufiksu, np. 3.8, są uznawane za 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ść między 8-bitowym (byte), 32-bitowym (int) i 64-bitowym (long). Dlatego 256 jest uznawane za int, ale 255 + 1 powoduje przepełnienie do 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 reinterpretowane jako wartości bez znaku. Dlatego 0xffffffff ma wartość int -1. Od Androida 13 do stałych można dodawać sufiks u8, np. 3u8, aby reprezentować wartość byte. Ten sufiks jest ważny, aby obliczenie, np. 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 Java. W kolejności od najniższego do
najwyższego priorytetu operatory binarne to
|| && | ^ & == != < > <= >= << >> + - * / %. Operatory jednoargumentowe to + - ! ~.