Aidl (Язык Определения Интерфейса Android) И Межпроцессное Взаимодействие (Ipc)

В этой статье мы попытаемся описать наш опыт работы с AIDL в Android IPC. Он содержит пример приложения со службой, работающей в отдельном процессе.

Статью следует рассматривать как:

  • пример архитектуры приложения с использованием удаленных служб Android и AIDL.
  • полезные примеры кода.

  • исключительно как дополнение к основной документации по Android Developers (см.

    ссылки в конце статьи).



Базовые концепты
Сервис — компонент Android-приложения без пользовательского интерфейса, предназначенный для выполнения ресурсоемких и/или трудоемких операций.

Типы Android-сервисов

  • Запущенные — сервисы, которые запускаются любым другим компонентом приложения (Activity, BrodcastReceiver, Service) и работают до тех пор, пока не остановятся сами или их кто-то не остановит.
  • Связанный (связанный) — сервис, выполняющий роль сервера в архитектуре клиент-сервер.

    Этот сервис создается при первом подключении (запросе) от другого компонента приложения.

    Служба останавливается, когда последний клиент отключается.

  • Служба может быть одновременно запущена и привязана.

    Такой сервис способен «жить вечно» и обслуживать запросы клиентов.

Сервис можно запустить в отдельном от Activity процессе.

Преимущества:

  • максимальный размер памяти увеличивается в 2 раза, например 32 МБ/процесс (в зависимости от платформы).

  • GC ведет себя менее агрессивно, если у вас есть 2 процесса со снимком N МБ каждый, чем 1 процесс и 2*N МБ.

  • стандартные преимущества сервисов Android: фон, независимость от Activity и т.д.
Недостатки:
  • дополнительные системные ресурсы для низкоуровневой сериализации и десериализации.

  • необходимость контроля жизненного цикла процесса.

  • еще немного кода.

АИДЛ В буквальном переводе это язык описания интерфейсов Android. Используется для описания композиции и декомпозиции объектов Java на примитивы ОС для прямой передачи между процессами.

Файлы AIDL очень похожи на стандартные интерфейсы Java, за исключением:

  • Вам необходимо импортировать даже те файлы helpl, которые находятся в одном пакете.

  • Ключевое слово oneway в объявлении метода void означает, что метод будет вызываться асинхронно (клиент не ждет его выполнения).

  • Вы можете использовать только примитивы, классы String, List и Parcelable, объявленные в других вспомогательных файлах.

С помощью AIDL автоматически генерируется Java-код для генерации стабов.



Архитектура приложения
Разработанное нами приложение представляет собой галерею для Android, позволяющую просматривать фотографии с карты памяти и сетей обмена фотографиями.

Основными задачами сервиса в этом приложении являются: получение метаданных (информации об альбомах, фотографиях, друзьях), отслеживание их обновлений и всего остального, что с ними связано.

Сервис постоянно хранит актуальную информацию и готов в любой момент передать ее основной Activity для отображения.

Ниже приведены ключевые участки кода и процесс создания примитивного сервиса: Для связи между сервисом и Activity используются следующие файлы AIDL: IDataSourceService.aidl – интерфейс сервиса:

  
  
  
  
  
  
  
   

packagecom.umobisoft.habr.aidlexample.common; import com.umobisoft.habr.aidlexample.common.IDataSourceServiceListener; interfaceIDataSourceService{ voidloadAlbums(in IDataSourceServiceListener listener); … }

IDataSourceServiceListener.aidl — интерфейс для прослушивателей сообщений от сервиса:

package com.umobisoft.habr.aidlexample.common; import com.umobisoft.habr.aidlexample.common.pojo.Album; interface IDataSourceServiceListener{ oneway void albumItemLoaded(in Album a); }

Данные передаются с помощью двух классов, реализующих интерфейс Parcelable, — Album и Photo. Требуется объявление вспомогательных файлов для этих классов.

При преобразовании примитивов ОС в объекты Java используется класс Creator. Для записи данных используйте метод writeToParcel интерфейса Parcelable:

@Override public void writeToParcel(Parcel out, int flags) { try{ out.writeLong(id); out.writeString(name); out.writeTypedList(photos); }catch (Exception e) { Log.e(TAG, "writeToParcel", e); } }

Также есть вспомогательный методdescriptionContents, его задача — описать особые случаи/состояния объекта, которые можно использовать при сериализации и десериализации:

@Override public int describeContents() { // TODO Auto-generated method stub return 0; }

Методы чтения данных оказались недостойными включения в интерфейс Parcelable, но стандартной практикой является использование Creator вместе с:

private void readFromParcel(Parcel in) { try{ id = in.readLong(); name = in.readString(); photos.clear(); in.readTypedList(photos, Photo.CREATOR); }catch (Exception e) { Log.e(TAG, "readFromParcel", e); } }

Действие запускает службу (таким образом, StartedService) в методе onCreate.

@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); textView = (TextView)findViewById(R.id.album_text); Intent serviceIntent = newIntent(this, DataSourceService.class); startService(serviceIntent); connectToService(); }

На этом же этапе жизненного цикла он подключается к сервису:

private void connectToService() { Intent intent = newIntent(this, DataSourceService.class); this.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE); }

Процесс подключения к сервису асинхронный; он включает реализацию интерфейса ServiceConnection. При подключении к серверу Activity регистрируется в службе в качестве прослушивателя сообщений с использованием реализации IDataSourceServiceListener.Stub:

private ServiceConnection serviceConnection = newServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.i(TAG, "Service connection established"); serviceApi = IDataSourceService.Stub.asInterface(service); try { mainListener = newIDataSourceServiceListener.Stub() { @Override publicvoidalbumItemLoaded(final Album a) throwsRemoteException { mToastHandler.post(new Thread(){ publicvoid run(){ Toast.makeText(HabrahabrAIDLExampleActivity.this, a.toString(), Toast.LENGTH_LONG).

show(); textView.setText(a.toString()); } }); } }; serviceApi.loadAlbums(mainListener); } catch (RemoteException e) { Log.e(TAG, "loadAlbums", e); } } @Override publicvoidonServiceDisconnected(ComponentName name) { Log.i(TAG, "Service connection closed"); serviceApi = null; connectToService(); } };

Если во время работы StartedService объем свободной памяти уменьшится до определенного порога, система может закрыть службу без предупреждения.

После этого система должна перезапустить службу.

Таким образом, в методе onServiceDisconnected мы снова инициализируем соединение с сервисом.

Надеюсь, выложенные исходные тексты помогут разработчикам познакомиться с AIDL. Архив с полным примером здесь .

Основное приложение доступно для ознакомления по адресу Android Market .

Официальная документация по Android Developers: Услуги: Developer.android.com/guide/topics/fundamentals/services.html АИДЛ: Developer.android.com/guide/developing/tools/aidl.html Теги: #Android #AIDL #IPC #Разработка Android

Вместе с данным постом часто просматривают: