В данной статье мы попытаемся описать свой опыт работы с AIDL в Android IPC.
В ней содержится пример приложения с сервисом, который запущен в отдельном процессе.
Статью стоит рассматривать как:
Service – это компонентAndroid приложения без интерфейса пользователя, предназначенныйдля осуществления ресурсоемких и/или длительных операций.
Сервис может быть запущен в отдельном от Activity процессе.
Преимущества:
Недостатки:
В буквальном переводе – язык описания интерфейсов Android. Используется для описания композиции и декомпозиции Java объектов в примитивы ОС для непосредственно передачи между процесами.
AIDL файлы очень похожи на стандартные интерфейсы в java за исключением:
С помощью AIDL автоматически генерируется java код для генерации stab’ов.
Разработанное нами приложение — галерея для Android которая позволяет просматривать фотографии из карты памяти и сетей обмена фотографиями.
Основными задачами сервиса в данном приложении являются: получение метаданных (информации о альбомах, фотографиях, друзьях), мониторинг их обновл��ний и всего остального, что с ними связано. Сервис постоянно хранит актуальную информацию и готов в любой момент отдать ее основной Activity для отображения.
Ниже будут приведены ключевые участки кода и описан процесс создания примитивного сервиса:
Для осуществления общения между сервисом и Activity используются следующие AIDL файлы:
IDataSourceService.aidl – интерфейс сервиса:
IDataSourceServiceListener.aidl – интерфейс слушателей сообщений от сервиса:
Данные передаются с помощью двух классов, которые реализуют интерфейс Parcelable — Album и Photo. Декларация aidl файлов для этих классов обязательна. При конвертации из примитивов ОС в java Объекты используется класс Creator.
Для записи данных используется метод writeToParcel интерфейса Parcelable:
Также существует вспомогательный метод describeContents, его задача описать специальные случаи/состояния объектакоторые когут использоватся при сериализации и десириализации:
Методы чтения данных оказались недостойными вынесения их в состав интерфейса Parcelable, но стандартная практика – использование Creator вместе с:
Activity запускает сервис (делая его таким образом StartedService) в методе onCreate
На этом же этапе жизненного цикла она соединяется с сервисом:
Процесс соединения с сервисом асинхронный, в нем учувствует реализация интерфейса ServiceConnection. Во время соединения с сервером Activity регистрируется в сервисе как слушатель сообщений, с помощью имплементации IDataSourceServiceListener.Stub:
Во время работы StartedService, в случае если к-во свободной памяти уменьшатся до определенного порога, система может убить сервис без предупреждения. После этого система обязана перезапустить сервис. Таким образом, в методе onServiceDisconnected мы опять инициализируем связь с сервисом.
Надеюсь выложенные исходные тексты помогут разработчикам ознакомится с AIDL. Архив с полным примером тут.
Основное приложение доступно для ознакомления в Android Market.
Официальная документация на AndroidDevelopers:
Services: developer.android.com/guide/topics/fundamentals/services.html
AIDL: developer.android.com/guide/developing/tools/aidl.html
В ней содержится пример приложения с сервисом, который запущен в отдельном процессе.
Статью стоит рассматривать как:
- пример архитектуры приложения, использующего remote Android Services и AIDL.
- полезные примеры кода.
- исключительно как дополнение к основной документации на Android Developers (см. ссылки в конце статьи).
Базовые понятия
Service – это компонентAndroid приложения без интерфейса пользователя, предназначенныйдля осуществления ресурсоемких и/или длительных операций.
Типы Android сервисов
- Started (запущенные) — сервисы которые запускаются любым другим компонентом приложения (Activity, BrodcastReceiver, Service) и работают пока не остановят сами себя или кто-то не остановит их.
- Bound (связанные) — сервис который выступает в роли сервера в клиент-серверной архитектуре. Такой сервис создается при первом соединении(запросе) от другого компонента приложения.Сервис останавливается, когда отсоединится последний клиент.
- Сервис может быть одновременно и Started и Bound. Такой сервис способен «жить вечно» и обслуживать запросы клиентов.
Сервис может быть запущен в отдельном от Activity процессе.
Преимущества:
- максимальный размер памяти увеличивается в 2 раза, например 32 МБ/процесс (зависит от платформы).
- GC ведет себя менее агрессивно, если у вас есть 2 процесса со snapshot в N МБкаждый, чем 1 процесс и 2*N МБ.
- стандартные преимущества Android сервисов: background, независимость от Activity, прочее.
Недостатки:
- дополнительные ресурсы системы на низкоуровневую сериализацию и десериализацию.
- необходимость контроля жизненного цикла процесса.
- немного больше кода.
AIDL
В буквальном переводе – язык описания интерфейсов Android. Используется для описания композиции и декомпозиции Java объектов в примитивы ОС для непосредственно передачи между процесами.
AIDL файлы очень похожи на стандартные интерфейсы в java за исключением:
- Импортировать нужно даже те aidl файлы, которые находятся в том же пакете.
- Ключевое слово oneway в декларации void метода означает что метод будет вызван асинхронно (клиент не дожидается его выполнения).
- Использовать можно только примитивы, String, List и Parcelable классы, объявленные в других aidl файлах.
С помощью AIDL автоматически генерируется java код для генерации stab’ов.
Архитектура приложения
Разработанное нами приложение — галерея для 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. Декларация aidl файлов для этих классов обязательна. При конвертации из примитивов ОС в 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); } }
Также существует вспомогательный метод describeContents, его задача описать специальные случаи/состояния объектакоторые когут использоватся при сериализации и десириализации:
@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); } }
Activity запускает сервис (делая его таким образом 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.
Официальная документация на AndroidDevelopers:
Services: developer.android.com/guide/topics/fundamentals/services.html
AIDL: developer.android.com/guide/developing/tools/aidl.html
