Вторая часть статьи

Приложения с расширяемой функциональностью — обыденная вещь в настольных ОС. Большинство сложных, узкоспециализированных приложений позволяют подключать дополнения в виде плагинов или скриптов. Это удобная, важная и легко реализуемая функция, которая в простейшем случае может быть встроена в приложение с помощью нескольких строк, всего лишь загружающих внешнюю библиотеку. Но как с этим обстоят дела в Android, где каждое приложение замкнуто в свою собственную песочницу, а распространять «просто библиотеки» через маркет нельзя?

 

Введение

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

Первый способ заключается в том, чтобы встроить в приложение интерпретатор простого скриптового языка (Lua лучше всего годится на эту роль). Главная фишка этого способа — простота разработки плагинов. Но есть и ряд недостатков:

  • Необходимо реализовать биндинги к функциям своего приложения и коллбэки в обратную сторону, что может оказаться совсем не простой задачей.
  • Невозможно распространять плагины через маркет, придется либо просить пользователей самостоятельно копировать скрипты в память устройства, либо кодить свой собственный репозиторий и платить за хостинг.
  • Производительность скриптов не самая высокая.
  • Тебе, скорее всего, придется иметь дело с интерпретатором, написанным на C/C++, и самостоятельно собирать его для разных процессорных архитектур.

В общем, не самый удачный, но имеющий право на существование способ.

Второй способ основан на встроенном в среду исполнения механизме динамической загрузки классов. Он позволяет разбить приложение на множество модулей, которые будут подгружены прямо во время его работы. По сути этот способ очень похож на тот, что используется в приложениях для настольных ОС, и имеет те же недостатки:

  • Модули не получится распространять через маркет.
  • После загрузки модули становятся частью приложения, а значит, тебе придется самостоятельно ограничивать их права, вводить квоты на время исполнения и написать много другого кода, следящего за поведением модуля и ограничивающего его возможности.
  • Необходимо будет не только тщательно продумать API модулей, но и следить за его соблюдением. Система подключит модуль, невзирая на любые несостыковки в API, а попытка вызвать несуществующую функцию или функцию, принимающую другой тип аргумента, приведет к падению всего приложения.

Тем не менее модули могут быть очень полезны при разработке разного рода бэкдоров и троянов (в образовательных целях, естественно), поэтому тему разработки модульных приложений, основанных на прямой загрузке классов, мы все-таки рассмотрим, но в следующий раз. А сегодня поговорим о третьем способе, самом удобном и полностью соответствующем идеологии Android. Это плагины, подключаемые с помощью RPC-механизма Binder.

DashClock: одно из самых известных приложений с поддержкой плагинов
DashClock: одно из самых известных приложений с поддержкой плагинов
 

Плагины и Binder

Binder — это механизм, с помощью которого в Android реализована система обмена сообщениями и вызова методов между приложениями и системными компонентами. Подробно о Binder я писал в статье, посвященной логированию звонков, однако рассказал только об обмене данными с его помощью. Если же мы хотим применить его для реализации плагинов, нам необходимо разобраться, как использовать возможность удаленного вызова процедур (RPC). Но сначала определимся с архитектурой нашего приложения.

Допустим, у нас есть гипотетическая софтина, к которой нужно прикрутить поддержку плагинов. Каждый плагин должен быть полноценным приложением для Android (с собственным графическим интерфейсом или без, на усмотрение разработчика), так что его тоже можно будет распространять через маркет. Плагины должны поддерживать определенный нами API, с помощью которого приложение сможет вызвать его функции. Приложение должно уметь само находить установленные плагины и добавлять их в список.

С учетом сказанного нам необходимо внести в код приложения следующие изменения:

  1. Определить API, с помощью которого приложение будет общаться с плагинами.
  2. Реализовать механизм поиска плагинов и вести их «учет».
 

API

Самый удобный и простой способ реализации плагинов — в виде сервисов, запускаемых по запросу. Наше приложение будет находить установленные в системе приложения-плагины, в нужные моменты запускать реализованные в них сервисы и вызывать их функции. При этом сервисы будут запущены только тогда, когда они действительно нужны, а система сама позаботится об их завершении и менеджменте ресурсов. Именно так, кстати, работает система плагинов виджета DashClock и многих других приложений.

Вызов функций будет происходить с помощью Binder, а это, как я уже сказал, RPC-механизм, и он требует описания API (интерфейса) с каждой из сторон (приложения и плагинов) с помощью языка AIDL (Android Interface Definition Language). Сам AIDL очень прост, и описание интерфейса на нем почти ничем не отличается от Java. Для нашего примера создадим простой интерфейс, определяющий две функции: run() и name():

package com.example.plugin

interface IPlugin {
  // Возвращает имя плагина
  String name();
  // Запускает плагин и возвращает результат работы
  String run(int seconds);
}

Создай файл IPlugin.aidl с помощью New → AIDL → AIDL file и помести в него эти строки. Затем выполни Build → Make Project, чтобы Android Studio преобразовал AIDL в обычный код на Java.

 

Простейший плагин

Теперь реализуем сам плагин. Для этого создаем новый проект (пусть его имя будет com.example.plugin1), добавляем в него файл IPlugin.aidl (обрати внимание, что он должен точно совпадать с аналогичным файлом из предыдущего раздела) и файл Plugin.java со следующим содержимым:

public class Plugin extends Service {
  @Override
  public void onCreate() {
    super.onCreate();
  }

  @Override
  public IBinder onBind(Intent intent) {
    return mBinder;
  }

  private final IPlugin.Stub mBinder = new IPlugin.Stub() {
    public String name() {
      return "ExamplePlugin";
    }

    public String run(int seconds) {
      try {
        Thread.sleep(seconds * 1000);
      } catch (Exception e) {}
      return "plugin1 done";
    }
  };
}

Это простейший плагин, который просто засыпает при запуске. Наиболее важная его часть — это метод onBind, который возвращает объект класса Binder, реализующий наш интерфейс IPlugin, в момент подключения к сервису. Другими словами, при подключении к плагину наше приложение получит объект, с помощью которого сможет вызывать определенные в плагине функции name() и run().

 

Поиск плагинов

Теперь нам необходимо реализовать систему поиска установленных плагинов. Проще (и правильнее) всего сделать это с помощью интентов, о которых можно прочитать в упомянутой выше статье. Для этого сначала внесем изменения в файл Manifest нашего плагина, добавив в него следующие строки (в раздел application):

Продолжение статьи доступно только подписчикам

Вариант 1. Оформи подписку на «Хакер», чтобы читать все статьи на сайте

Подписка позволит тебе в течение указанного срока читать ВСЕ платные материалы сайта, включая эту статью. Мы принимаем оплату банковскими картами, электронными деньгами и переводами со счетов мобильных операторов. Подробнее о подписке

Вариант 2. Купи одну статью

Заинтересовала статья, но нет возможности оплатить подписку? Тогда этот вариант для тебя! Обрати внимание: этот способ покупки доступен только для статей, опубликованных более двух месяцев назад.


3 комментария

Подпишитесь на ][, чтобы участвовать в обсуждении

Обсуждение этой статьи доступно только нашим подписчикам. Вы можете войти в свой аккаунт или зарегистрироваться и оплатить подписку, чтобы свободно участвовать в обсуждении.

Check Also

WWW: TLDR pages — замена справке man, показывающая только самое важное

Что ты делаешь каждый раз, когда набираешь команду man в Linux или Unix? Правильно: тяжело…