Le langage AIDL est vaguement basé sur le langage Java. Les fichiers spécifient un contrat d'interface, ainsi que différents types de données et constantes utilisés dans ce contrat.
Package
Chaque fichier AIDL commence par un package facultatif qui correspond aux noms de package dans différents backends. Une déclaration de package se présente comme suit :
package my.package;
Comme pour Java, les fichiers AIDL doivent se trouver dans une structure de dossiers correspondant à leur package. Les fichiers avec le package my.package doivent se trouver dans le dossier my/package/.
Types
Dans les fichiers AIDL, les types peuvent être spécifiés à de nombreux endroits. Pour obtenir la liste exacte des types compatibles avec le langage AIDL, consultez la section Types de backends AIDL.
Annotations
Plusieurs parties du langage AIDL sont compatibles avec les annotations. Pour obtenir la liste des annotations et des endroits où elles peuvent être appliquées, consultez la section Annotations AIDL.
Importations
Pour utiliser des types définis dans d'autres interfaces, vous devez d'abord ajouter des dépendances dans le système de compilation. Dans les modules Soong cc_* et java_*, où les fichiers .aidl sont utilisés directement sous srcs dans les builds de la plate-forme Android, vous pouvez ajouter des répertoires à l'aide du champ aidl: { include_dirs: ... }. Pour les importations à l'aide de
aidl_interface, cliquez
ici.
Une importation se présente comme suit :
import some.package.Foo; // explicit import
Lors de l'importation d'un type dans le même package, le package peut être omis. Toutefois, l'omission du package peut entraîner des erreurs d'importation ambiguës lorsque les types sont spécifiés sans package et placés dans l'espace de noms global (en général, tous les types doivent être dans un espace de noms) :
import Foo; // same as my.package.Foo
Définir des types
Les fichiers AIDL définissent généralement des types qui sont utilisés comme interface.
Interfaces
Voici un exemple d'interface 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();
}
Une interface définit un objet avec une série de méthodes. Les méthodes peuvent être
oneway (oneway void doFoo()) ou synchrones. Si une interface est définie comme
oneway (oneway interface ITeleport {...}), toutes ses méthodes sont
implicitement oneway. Les méthodes unidirectionnelles sont distribuées de manière asynchrone et ne peuvent pas renvoyer de résultat. Les méthodes unidirectionnelles du même thread vers le même binder s'exécutent également en série (bien que potentiellement sur des threads différents). Pour savoir comment configurer des threads, consultez la section Gestion des threads des backends AIDL
management.
Binder permet de partager de nombreuses interfaces et objets binder via des interfaces binder. Les interfaces AIDL utilisent fréquemment des rappels dans le cadre d'appels de méthode, comme avec ITeleportCallback dans l'exemple précédent. Vous pouvez réutiliser des objets de rappel entre des appels à la même méthode ou à différentes méthodes. Une autre utilisation courante des types d'interface consiste à renvoyer des sous-interfaces ou des objets de session à partir de méthodes telles que ITeleportSession dans l'exemple précédent. Cette imbrication permet d'encapsuler différentes API au niveau de l'API ou en fonction de l'état d'exécution. Par exemple, une session peut représenter la propriété d'une ressource particulière. Lorsque les interfaces sont transmises plusieurs fois ou renvoyées au client ou au serveur d'où elles proviennent, elles conservent toujours l'égalité des pointeurs de l'objet binder sous-jacent.
Les méthodes peuvent avoir zéro ou plusieurs arguments. Les arguments des méthodes peuvent être
in, out ou inout. Pour savoir comment cela affecte les types d'arguments,
consultez
la section Directionnalité des backends AIDL.
Parcelables
Pour savoir comment créer des parcelables spécifiques au backend, consultez la section Parcelables personnalisés des backends AIDL .
Android 10 et versions ultérieures sont compatibles avec les définitions parcelables directement dans AIDL. Ce type de parcelable est appelé parcelable structuré. Pour en savoir plus sur la relation entre AIDL structuré et stable dans le compilateur AIDL et notre système de compilation, consultez la section AIDL structuré par rapport à AIDL stable.
Exemple :
package my.package;
import my.package.Boo;
parcelable Baz {
@utf8InCpp String name = "baz";
Boo boo;
}
Syndicats
Android 12 et versions ultérieures sont compatibles avec les déclarations de syndicats balisés. Exemple :
package my.package;
import my.package.FooSettings;
import my.package.BarSettings;
union Settings {
FooSettings fooSettings;
BarSettings barSettings;
@utf8InCpp String str;
int number;
}
Pour obtenir des informations spécifiques au backend, consultez la section Syndicats des backends AIDL.
Enums
Android 11 et versions ultérieures sont compatibles avec les déclarations d'énumérations. Exemple :
package my.package;
enum Boo {
A = 1 * 4,
B = 3,
}
Déclarations de types imbriquées
Android 13 et versions ultérieures sont compatibles avec les déclarations de types imbriquées. Exemple :
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
}
Constantes
Les interfaces, parcelables et syndicats AIDL personnalisés peuvent également contenir des constantes entières et de chaîne, telles que :
const @utf8InCpp String HAPPY = ":)";
const String SAD = ":(";
const byte BYTE_ME = 1;
const int ANSWER = 6 * 7;
Expressions constantes
Les constantes, les tailles de tableaux et les énumérateurs AIDL peuvent être spécifiés à l'aide d'expressions constantes. Les expressions peuvent utiliser des parenthèses pour imbriquer des opérations. Les valeurs d'expression constante peuvent être utilisées avec des valeurs entières ou flottantes.
Les littéraux true et false représentent des valeurs booléennes. Les valeurs avec un . mais
sans suffixe, telles que 3.8, sont considérées comme des valeurs doubles. Les valeurs flottantes
ont le suffixe f, comme 2.4f. Une valeur intégrale avec le l ou
L suffixe indique une valeur longue de 64 bits. Sinon, les valeurs intégrales obtiennent le plus petit type signé préservant la valeur entre 8 bits (octet), 32 bits (int) et 64 bits (long). Ainsi, 256 est considéré comme un int, mais 255 + 1 dépasse la capacité et devient le byte 0. Les valeurs hexadécimales, telles que 0x3, sont d'abord interprétées comme le plus petit type non signé préservant la valeur entre 32 et 64 bits, puis réinterprétées comme des valeurs non signées. Ainsi, 0xffffffff a la valeur int -1. À partir d'Android 13, le suffixe u8 peut être
ajouté aux constantes, telles que 3u8, pour représenter une valeur byte. Ce suffixe est important pour qu'un calcul, tel que 0xffu8 * 3, soit interprété comme -3 avec le type byte, tandis que 0xff * 3 est 765 avec le type int.
Les opérateurs compatibles ont une sémantique C++ et Java. Par ordre de priorité croissant, les opérateurs binaires sont
|| && | ^ & == != < > <= >= << >> + - * / %. Les opérateurs unaires sont + - ! ~.