Android AIDL file

Android AIDL

Android AIDL

AIDL – язык определения интерфейсов в Android. С помощью языка определения интерфейсов мы определяем программный интерфейс, благодаря которому клиент и Android сервис могут взаимодействовать друг с другом. И необходимо понимать, что AIDL нужен только если необходимо другим приложениям обеспечить доступ к нашему сервису или если необходима многопоточность в нашем приложении(VPN приложение отличный кандидат на многопоточность в сервисе).

Алгоритм предельно прост:

  1. Создаем AIDL файл
  2. реализуем интерфейс
  3. Предоставляем реализацию клиентам

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> как один из самых распространенных вариантов.

Android AIDL Service

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

Android AIDL file

Теперь Мы выполним сборку нашего проекта – 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 {

      }

   };
}

 

Android AIDL Service Implementation

Теперь покажем, как необходимо подключаться к нашему сервису… Выполняем 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 службы

  1. Создать все необходимые  .aidlфайлы в src/aidl каталоге проекта .
  2. Объявляем экземпляр интерфейсаIBinder (созданный на основе AIDL файла).
  3. Реализация ServiceConnection.
  4. Вызов Context.bindService(), для связи с удаленным сервисом.
  5. В реализации onServiceConnected()получаем экземпляр IBinder (называемый сервисом). Далее приводим тип к типу нашего интерфейса:  YourInterface .YourInterfaceName.Stub.asInterface((IBinder)service)
  6. Вызываем удаленные сервисы. Не забываем перехватывать исключения DeadObjectException, которые возникают при разрыве соединения. Мы также должны перехватывать исключения  SecurityException которые возникают, когда два процесса, участвующие в вызове метода IPC, имеют конфликт в определении AIDL интерфейса.
  7. Чтобы отсоеденииться от удаленного сервиса, вызываем Context.unbindService().

 

Leave a Reply

Please disable your adblocker or whitelist this site!