El lenguaje AIDL se basa en gran medida 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
Todos los archivos AIDL comienzan con un paquete opcional que corresponde a los nombres de paquetes en varios backends. Una declaración de paquete se ve de la siguiente manera:
package my.package;
Al igual que en 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 donde se pueden especificar tipos. Para obtener una lista exacta de los tipos compatibles con el lenguaje de 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 de Soong cc_* y java_*, en los que los archivos .aidl se usan directamente en srcs en las 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 se importa 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 (en general, todos los tipos deben tener un espacio de nombres):
import Foo; // same as my.package.Foo
Cómo definir tipos
Por lo general, los archivos AIDL definen tipos que se usan como interfaz.
Interfaces
A continuación, se muestra 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 implícitamente oneway. Los métodos unidireccionales se envían de forma asíncrona y no pueden devolver un resultado. Los métodos unidireccionales del mismo subproceso al mismo vinculador también se ejecutan de forma serial (aunque posiblemente en diferentes subprocesos). Para obtener información sobre cómo configurar subprocesos, consulta Administración de subprocesos de back-ends de AIDL.
Binder permite que se compartan muchas interfaces y objetos de Binder a través de interfaces de Binder. Las interfaces de AIDL suelen emplear devoluciones de llamada como parte de las llamadas a métodos, como con ITeleportCallback en el ejemplo anterior. Puedes reutilizar objetos de devolución de llamada entre llamadas al mismo método o a diferentes métodos. Otro uso común de los tipos de interfaz es para que los métodos devuelvan subinterfaces o objetos de sesión, como con ITeleportSession en el ejemplo anterior. Este anidamiento permite encapsular diferentes APIs en la API o según el estado del tiempo 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 del que provienen, siempre conservan la igualdad de puntero del objeto binder subyacente.
Los métodos pueden tener cero o más argumentos. Los argumentos para los métodos pueden ser in, out o inout. Para obtener información sobre cómo esto afecta 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 de objetos parcelables directamente en AIDL. Este tipo de objeto Parcelable se denomina objeto Parcelable estructurado. Para obtener más información sobre cómo se relacionan el AIDL estructurado y el estable en el compilador de AIDL y nuestro sistema de compilación, consulta AIDL estructurado versus estable.
Por ejemplo:
package my.package;
import my.package.Boo;
parcelable Baz {
@utf8InCpp String name = "baz";
Boo boo;
}
Sindicatos
Android 12 y 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 AIDL Backends Unions para obtener detalles específicos del backend.
Enums
Android 11 y versiones posteriores admiten declaraciones de enumeración. Por ejemplo:
package my.package;
enum Boo {
A = 1 * 4,
B = 3,
}
Declaraciones de tipos anidadas
Android 13 y versiones posteriores admiten declaraciones de tipos anidadas. 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 parcelables y las uniones de AIDL personalizados 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 arrays 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 constantes se pueden usar con valores integrales o de coma flotante.
Los literales true y false representan valores booleanos. Los valores con un ., pero sin un sufijo, como 3.8, se consideran valores dobles. Los valores de coma flotante tienen el sufijo f, como 2.4f. Un valor integral con el sufijo l o L indica un valor largo de 64 bits. De lo contrario, los valores integrales obtienen el tipo firmado más pequeño que conserva el valor entre 8 bits (byte), 32 bits (int) y 64 bits (long). 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 signo más pequeño que conserva el valor entre 32 y 64 bits, y luego se vuelven a interpretar como valores sin signo. Por lo tanto, 0xffffffff tiene el valor int -1. A partir de Android 13, se puede agregar el sufijo u8 a las 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 precedencia, de menor a mayor, los operadores binarios son || && | ^ & == != < > <= >= << >> + - * / %. Los operadores unarios son + - ! ~.