El lenguaje AIDL se basa en líneas generales en el lenguaje Java. Los archivos especifican un contrato de interfaz y varios tipos de datos y constantes que se usan en este contrato.
Paquete
Cada archivo AIDL comienza con un paquete opcional que corresponde a los nombres de los paquetes en varios backends. Una declaración de paquete se ve de la siguiente manera:
package my.package;
Al igual que Java, los archivos AIDL deben estar en una estructura de carpetas que coincida con su paquete. Los archivos con el paquete my.package
deben estar en la carpeta my/package/
.
Tipos
En los archivos AIDL, hay muchos lugares en los que se pueden especificar los tipos. Para obtener una lista exacta de los tipos compatibles con el lenguaje AIDL, consulta Tipos de backends de AIDL.
Anotaciones
Varias partes del lenguaje AIDL admiten anotaciones. Para obtener una lista de las anotaciones y dónde se pueden aplicar, consulta Anotaciones de AIDL.
Importaciones
Para usar los tipos definidos en otras interfaces, primero debes agregar dependencias en el
sistema de compilación. En los módulos Soong cc_*
y java_*
, en los que los archivos .aidl
se usan directamente en srcs
en compilaciones de la plataforma de Android, puedes agregar directorios con el campo aidl: { include_dirs: ... }
. Para las importaciones con aidl_interface
, consulta aquí.
Una importación se ve de la siguiente manera:
import some.package.Foo; // explicit import
Cuando importas un tipo en el mismo paquete, se puede omitir el paquete. Sin embargo, omitir el paquete puede generar errores de importación ambiguos cuando los tipos se especifican sin un paquete y se colocan en el espacio de nombres global (por lo general, todos los tipos deben tener espacio de nombres):
import Foo; // same as my.package.Foo
Define los tipos
Por lo general, los archivos AIDL definen tipos que se usan como interfaz.
Interfaces
Este es un ejemplo de interfaz de 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();
}
Una interfaz define un objeto con una serie de métodos. Los métodos pueden ser oneway
(oneway void doFoo()
) o síncronos. Si una interfaz se define como oneway
(oneway interface ITeleport {...}
), todos los métodos que contiene son oneway
de forma implícita. Los métodos unidireccionales se despachan de forma asíncrona y no pueden mostrar un resultado. Los métodos unidireccionales del mismo subproceso al mismo Binder también se ejecutan de forma serial (aunque potencialmente en diferentes subprocesos). Para obtener información sobre cómo configurar subprocesos, consulta Administración de subprocesos de backends de AIDL.
Binder permite que se compartan muchas interfaces y objetos de Binder a través de sus interfaces. Las interfaces del AIDL con frecuencia emplean devoluciones de llamada como parte de las llamadas de método, como con ITeleportCallback
en el ejemplo anterior. Puedes volver a usar objetos de devolución de llamada entre llamadas al mismo método o llamadas a diferentes métodos. Otro uso común de los tipos de interfaz es que las subinterfaces o los objetos de sesión se muestren desde métodos como con ITeleportSession
en el ejemplo anterior. Este anidamiento permite que se encapsulen diferentes APIs en la API o en función del estado del entorno de ejecución. Por ejemplo, una sesión puede representar la propiedad de un recurso en particular. Cuando las interfaces se pasan varias veces o se devuelven al cliente o servidor de donde provienen, siempre conservan la igualdad de punteros del objeto Binder subyacente.
Los métodos pueden tener cero o más argumentos. Los argumentos de los métodos pueden ser in
, out
o inout
. Para obtener información sobre cómo esto afecta a los tipos de argumentos, consulta Direccionalidad de los backends de AIDL.
Objetos parcelables
Para obtener una descripción de cómo crear objetos parcelables específicos del backend, consulta Objetos parcelables personalizados de backends de AIDL.
Android 10 y versiones posteriores admiten definiciones parcelables directamente en AIDL. Este tipo de elemento parcelable se denomina elemento parcelable estructurado. Para obtener más información sobre cómo se relacionan el AIDL estructurado y estable en el compilador de AIDL y nuestro sistema de compilación, consulta AIDL estructurado y estable.
Por ejemplo:
package my.package;
import my.package.Boo;
parcelable Baz {
@utf8InCpp String name = "baz";
Boo boo;
}
Uniones
Android 12 y las versiones posteriores admiten declaraciones de unión etiquetadas. Por ejemplo:
package my.package;
import my.package.FooSettings;
import my.package.BarSettings;
union Settings {
FooSettings fooSettings;
BarSettings barSettings;
@utf8InCpp String str;
int number;
}
Consulta Uniones de backends de AIDL para obtener detalles específicos del backend.
Enums
Android 11 y las versiones posteriores admiten declaraciones de enum. Por ejemplo:
package my.package;
enum Boo {
A = 1 * 4,
B = 3,
}
Declaraciones de tipos anidadas
Android 13 y las versiones posteriores admiten declaraciones de tipos anidados. Por ejemplo:
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
Las interfaces, los elementos parcelables y las uniones de AIDL personalizadas también pueden contener constantes de números enteros y cadenas, como las siguientes:
const @utf8InCpp String HAPPY = ":)";
const String SAD = ":(";
const byte BYTE_ME = 1;
const int ANSWER = 6 * 7;
Expresiones constantes
Las constantes, los tamaños de array y los enumeradores de AIDL se pueden especificar con expresiones constantes. Las expresiones pueden usar paréntesis para anidar operaciones. Los valores de expresión constante se pueden usar con valores integrales o flotantes.
Los literales true
y false
representan valores booleanos. Los valores con un .
, pero sin sufijo, como 3.8
, se consideran valores dobles. Los valores de número de punto flotante tienen el sufijo f
, como 2.4f
. Un valor integral con el sufijo l
o L
indica un valor de 64 bits. De lo contrario, los valores integrales obtienen el tipo de firma más pequeño que preserva el valor entre 8 bits (byte), 32 bits (int) y 64 bits (largo). Por lo tanto, 256
se considera un int
, pero 255 + 1
se desborda para ser el byte
0
. Los valores hexadecimales, como 0x3
, primero se interpretan como el tipo sin firmar más pequeño que conserva el valor entre 32 y 64 bits y, luego, se reinterpretan como valores sin firmar. Por lo tanto, 0xffffffff
tiene el valor int
-1
. A partir de Android 13, se puede agregar el sufijo u8
a constantes, como 3u8
, para representar un valor byte
. Este sufijo es importante para que un cálculo, como 0xffu8 * 3
, se interprete como -3
con el tipo byte
, mientras que 0xff * 3
es 765
con el tipo int
.
Los operadores admitidos tienen semántica de C++ y Java. En orden de prioridad de la más baja a la más alta, los operadores binarios son || && | ^ & == != < > <= >= << >> + - * / %
. Los operadores unarios son + - ! ~
.