Il linguaggio AIDL si basa vagamente sul linguaggio Java. I file specificano un contratto di interfaccia e vari tipi di dati e costanti utilizzati in questo contratto.
Pacco
Ogni file AIDL inizia con un pacchetto facoltativo che corrisponde ai nomi dei pacchetti in vari backend. Una dichiarazione del pacchetto ha il seguente aspetto:
package my.package;
Analogamente a Java, i file AIDL devono trovarsi in una struttura di cartelle corrispondente al loro
pacchetto. I file con il pacchetto my.package devono trovarsi nella cartella my/package/.
Tipi
Nei file AIDL, ci sono molti punti in cui è possibile specificare i tipi. Per un elenco esatto dei tipi supportati nel linguaggio AIDL, consulta Tipi di backend AIDL.
Annotazioni
Diverse parti del linguaggio AIDL supportano le annotazioni. Per un elenco delle annotazioni e delle posizioni in cui possono essere applicate, vedi Annotazioni AIDL.
Importazioni
Per utilizzare i tipi definiti in altre interfacce, devi prima aggiungere le dipendenze nel sistema di build. Nei moduli Soong cc_* e java_*, in cui i file .aidl vengono utilizzati
direttamente in srcs nelle build della piattaforma Android, puoi aggiungere directory
utilizzando il campo aidl: { include_dirs: ... }. Per le importazioni che utilizzano
aidl_interface, vedi
qui.
Un'importazione ha il seguente aspetto:
import some.package.Foo; // explicit import
Quando importi un tipo nello stesso pacchetto, il pacchetto può essere omesso. Tuttavia, l'omissione del pacchetto può causare errori di importazione ambigui quando i tipi vengono specificati senza un pacchetto e inseriti nello spazio dei nomi globale (in genere tutti i tipi devono essere nello spazio dei nomi):
import Foo; // same as my.package.Foo
Definisci i tipi
I file AIDL in genere definiscono i tipi utilizzati come interfaccia.
Interfacce
Ecco un esempio di interfaccia 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();
}
Un'interfaccia definisce un oggetto con una serie di metodi. I metodi possono essere
oneway (oneway void doFoo()) o sincroni. Se un'interfaccia è definita come
oneway (oneway interface ITeleport {...}), tutti i metodi al suo interno sono
implicitamente oneway. I metodi unidirezionali vengono inviati in modo asincrono e non possono restituire un risultato. I metodi unidirezionali dello stesso thread allo stesso binder vengono eseguiti anche in serie (anche se potenzialmente su thread diversi). Per una
discussione su come configurare i thread, consulta Gestione dei thread dei backend AIDL.
Binder consente di condividere molte interfacce e oggetti binder tramite le interfacce binder. Le interfacce AIDL utilizzano spesso i callback come parte delle chiamate di metodo,
ad esempio con ITeleportCallback nell'esempio precedente. Puoi riutilizzare gli oggetti di callback tra chiamate allo stesso metodo o a metodi diversi. Un altro
utilizzo comune dei tipi di interfaccia è per le interfacce secondarie o
gli oggetti sessione da restituire da metodi come ITeleportSession nell'esempio precedente. Questo nesting consente di incapsulare API diverse
nell'API o in base allo stato di runtime. Ad esempio, una sessione può rappresentare
la proprietà di una risorsa specifica. Quando le interfacce vengono passate più volte o restituite al client o al server da cui provengono, mantengono sempre l'uguaglianza dei puntatori dell'oggetto binder sottostante.
I metodi possono avere zero o più argomenti. Gli argomenti dei metodi possono essere
in, out o inout. Per una discussione su come questo influisce sui tipi di argomenti,
vedi
Direzionalità dei backend AIDL.
Parcelable
Per una descrizione di come creare parcelable specifici per il backend, AIDL backends custom parcelables.
Android 10 e versioni successive supportano le definizioni parcelable direttamente in AIDL. Questo tipo di parcelable è chiamato parcelable strutturato. Per ulteriori informazioni sul rapporto tra AIDL strutturato e stabile nel compilatore AIDL e nel nostro sistema di build, vedi AIDL strutturato e stabile.
Ad esempio:
package my.package;
import my.package.Boo;
parcelable Baz {
@utf8InCpp String name = "baz";
Boo boo;
}
Sindacati
Android 12 e versioni successive supportano le dichiarazioni unione con tag. Ad esempio:
package my.package;
import my.package.FooSettings;
import my.package.BarSettings;
union Settings {
FooSettings fooSettings;
BarSettings barSettings;
@utf8InCpp String str;
int number;
}
Per informazioni specifiche sul backend, consulta Unioni dei backend AIDL.
Enumerazionis
Android 11 e versioni successive supportano le dichiarazioni enum. Ad esempio:
package my.package;
enum Boo {
A = 1 * 4,
B = 3,
}
Dichiarazioni di tipi nidificati
Android 13 e versioni successive supportano le dichiarazioni di tipo nidificate. Ad esempio:
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
}
Costanti
Le interfacce AIDL personalizzate, i parcelable e le unioni possono contenere anche costanti intere e stringhe, ad esempio:
const @utf8InCpp String HAPPY = ":)";
const String SAD = ":(";
const byte BYTE_ME = 1;
const int ANSWER = 6 * 7;
Espressioni costanti
Le costanti, le dimensioni degli array e gli enumeratori AIDL possono essere specificati utilizzando espressioni costanti. Le espressioni possono utilizzare le parentesi per nidificare le operazioni. I valori delle espressioni costanti possono essere utilizzati con valori interi o in virgola mobile.
I valori letterali true e false rappresentano valori booleani. I valori con un . ma
senza suffisso, ad esempio 3.8, sono considerati valori doppi. I valori
float hanno il suffisso f, ad esempio 2.4f. Un valore intero con il suffisso l o
L indica un valore lungo a 64 bit. In caso contrario, i valori integrali ottengono il tipo con segno che preserva il valore più piccolo tra 8 bit (byte), 32 bit (int) e 64 bit (long). Pertanto, 256 è considerato un int, ma 255 + 1
genera un overflow e diventa byte 0. I valori esadecimali, ad esempio 0x3, vengono prima interpretati
come il tipo senza segno più piccolo che preserva il valore tra 32 e 64 bit
e poi reinterpretati come valori senza segno. Quindi, 0xffffffff ha il valore int
-1. A partire da Android 13, il suffisso u8 può essere
aggiunto a costanti, ad esempio 3u8, per rappresentare un valore byte. Questo suffisso è
importante affinché un calcolo, ad esempio 0xffu8 * 3, venga interpretato come -3
con tipo byte, mentre 0xff * 3 è 765 con tipo int.
Gli operatori supportati hanno semantica C++ e Java. In ordine di precedenza crescente, gli operatori binari sono
|| && | ^ & == != < > <= >= << >> + - * / %. Gli operatori unari sono + - ! ~.