Android AIDL
AIDL – язык определения интерфейсов в Android. С помощью языка определения интерфейсов мы определяем программный интерфейс, благодаря которому клиент и Android сервис могут взаимодействовать друг с другом. И необходимо понимать, что AIDL нужен только если необходимо другим приложениям обеспечить доступ к нашему сервису или если необходима многопоточность в нашем приложении(VPN приложение отличный кандидат на многопоточность в сервисе).
Алгоритм предельно прост:
- Создаем AIDL файл
- реализуем интерфейс
- Предоставляем реализацию клиентам
AIDL интерфейс поддерживает следующие типы данных:
- Java примитивы(char,boolean,short и т.д.)
- Массивы Java примитивов(int[], double[] и т.д.)
- String
- CharSequence
- List(элементы List должны принадлежать к одному из поддерживаемых типов)
- Map(элементы Map должны принадлежать к одному из поддерживаемых типов). Если требуется более широкий функционал, то можно рассмотреть вариант использования Bundle в качестве альтернативы.
Также необходимо помнить про следующие пункты:
- методы могут принимать ноль и более параметров и могут как возвращать, так и нет данные(для примеры мы определим далее тип возвращаемого значения void и List<String>)
- Если мы используем непримитивные данные(объекты) – нам необходимо указывать в каком направлении идут данные с помощью ключевых слов in, out или inout. По умолчанию примитивы, String и IBinder имеет тип направления in
- Все комментарии кода, включенные в AIDL файл переносятся в сгенерированный IBinder интерфейс. Кроме комментариев перед импортом пакетов.
- Допускается определение констант типа String и int. Например const String PACKAGE = “it-nptepad.com”;
- Типы аргументов и возвращаемых значений, которые могут быть null, необходимо пометить аннотацией @nullable
Приступим:
Для примера представим реализацию сервиса, которая будет предоставлять клиентам список доступных видео камер(пример реальный из жизни). Теперь создадим новый проект в Android Studio. Создадим наш сервис – GetVideoList. Наша задача – предоставить возможность передачи List<String> как один из самых распространенных вариантов.

Теперь создадим наш AIDL файл. Необходимо помнить, что файлы AIDL необходимо размещать в отдельной папке – aidl. См. скринштот. Мы назовем его – RemoteVideoService.aidl.

Теперь Мы выполним сборку нашего проекта – Ctrl + 9. Смотрим папку java(generated) – теперь там появился наш пакет, определенный в AIDL файле -online.salattime.videoapp. После сборки у нас сгенерировался наш интерфейс, только уже в виде java файла – RemoteVideoService.java. Полный код ниже. Бояться его не стоит – у нас сгенерировалось автоматически много вспомогательных методов.
/*
* This file is auto-generated. DO NOT MODIFY.
*/
package online.salattime.videoapp;
public interface RemoteVideoService extends android.os.IInterface
{
/** Default implementation for RemoteVideoService. */
public static class Default implements online.salattime.videoapp.RemoteVideoService
{
/**
* Get's actually List of Id's camera videos.
*/
@Override public java.util.List<java.lang.String> getListVideos(int requestId, long transaction, java.lang.String hash) throws android.os.RemoteException
{
return null;
}
@Override public void exampleVoid(int id) throws android.os.RemoteException
{
}
@Override
public android.os.IBinder asBinder() {
return null;
}
}
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements online.salattime.videoapp.RemoteVideoService
...
/**
* Get's actually List of Id's camera videos.
*/
public java.util.List<java.lang.String> getListVideos(int requestId, long transaction, java.lang.String hash) throws android.os.RemoteException;
public void exampleVoid(int id) throws android.os.RemoteException;
}
Мы не включили более 100 строк кода, в связи с их абсолютной ненадобностью. Все интересующее нас привидено выше.
Теперь осталось реализовать наш интерфейс из сгенерированного AIDL файла:
private final RemoteVideoService.Stub binder = new RemoteVideoService.Stub() {
@Override
public List<java.lang.String> getListVideos(int requestId, long transaction, String hash) throws RemoteException {
return null;
}
@Override
public void exampleVoid(int id) throws RemoteException {
}
};
На этом все. Шутка) Мы же саму реализацию интерфейса для клиентов не сделали. Тут тоже особых сложностей не возникнет.
public class GetVideoList extends Service {
@Override
public void onCreate() {
super.onCreate();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return binder;
}
final RemoteVideoService.Stub binder = new RemoteVideoService.Stub() {
@Override
public List<java.lang.String> getListVideos(int requestId, long transaction, String hash) throws RemoteException {
return null;
}
@Override
public void exampleVoid(int id) throws RemoteException {
}
};
}

Теперь покажем, как необходимо подключаться к нашему сервису… Выполняем bindService() и получаем экзамерляр нашего интерфейса.
RemoteVideoService remoteService;
private ServiceConnection mConnection = new ServiceConnection() {
// Called when the connection with the service is established
public void onServiceConnected(ComponentName className, IBinder service) {
// Following the example above for an AIDL interface,
// this gets an instance of the IRemoteInterface, which we can use to call on the service
remoteService = RemoteVideoService.Stub.asInterface(service);
}
// Called when the connection with the service disconnects unexpectedly
public void onServiceDisconnected(ComponentName className) {
Log.e(TAG, "Service has unexpectedly disconnected");
remoteService = null;
}
}
На этом все. Однако я хочу задержаться на одном моменте, причем очень важном:
Передача объектов через IPC интерфейс
Что такое IPC? Точнее IPC интерфейс? IPC – это межпроцессное взаимодействие. Не путать с межпроцессорным. Начиная с Android 10, можно напрямую определять Parcelable объекты в AIDL файлах. Это позволяет нам избавиться от лишнего кода и лишней работы. Давайте попробуем создать наш Parcelable объект в AIDL файле. Назовем его VideoParcelable и определим в нем произвольные поля типа String, int, long, double.
// VideoParcelable.aidl
package online.salattime.videoapp;
parcelable VideoParcelable {
String videoLink;
int id;
long timeCreated;
double duration;
}
В папке gen после сборки проекта мы увидим сгенерированный автоматически файл VideoParcelable.java. Перед попыткой извлечения нашего объекта, не забудем вызвать Bundle.setClassLoader(ClassLoader), иначе получим исключение ClassNotFoundException. Например
private final VideoService.Stub binder = new VideoService.Stub() {
public void saveVideoObject(Bundle bundle){
bundle.setClassLoader(getClass().getClassLoader());
VideoParcelable videoObject = bundle.getParcelable("videoObject");
}
};
В заключении опишем алгоритм по вызову IPC службы
- Создать все необходимые
.aidlфайлы вsrc/aidlкаталоге проекта . - Объявляем экземпляр интерфейса
IBinder(созданный на основе AIDL файла). - Реализация
ServiceConnection. - Вызов
Context.bindService(), для связи с удаленным сервисом. - В реализации
onServiceConnected()получаем экземплярIBinder(называемый сервисом). Далее приводим тип к типу нашего интерфейса: YourInterface .YourInterfaceNameservice.Stub.asInterface((IBinder)) - Вызываем удаленные сервисы. Не забываем перехватывать исключения
DeadObjectException, которые возникают при разрыве соединения. Мы также должны перехватывать исключенияSecurityExceptionкоторые возникают, когда два процесса, участвующие в вызове метода IPC, имеют конфликт в определении AIDL интерфейса. - Чтобы отсоеденииться от удаленного сервиса, вызываем
Context.unbindService().