Андромеда предоставляет управление взаимодействием компонентов через интерфейсы, включая локальные вызовы внутри одного процесса и межпроцессные вызовы.
Примечание: Разделение на локальные сервисы и удаленные сервисы связано с тем, что интерфейсы локальных сервисов могут передавать различные типы аргументов и возвращаемых значений, тогда как удаленные интерфейсы ограничены AIDL и поддерживают только базовые типы данных или объекты, реализующие интерфейс Parcelable.
Не требуется от разработчиков выполнять bindService()
, достаточно определить aidl
-интерфейсы и их реализацию
Синхронное получение сервисов. Вместо асинхронного получения через bindService()
используется синхронное получение
Автоматическое управление жизненным циклом. Управление повышением или понижением приоритета сервисов в зависимости от жизненного цикла Fragment
или Activity
Поддержка обратных вызовов IPC
и событийной шины между процессами
Реализация взаимодействия компонентов через "интерфейс + структура данных". Это проще и удобнее для обслуживания по сравнению с протоколами
Примечание: Здесь сервисы не относятся к четырем основным компонентам Android, а указывают на предоставленные интерфейсы и их реализации. Для отличия, все последующие упоминания сервисов будут иметь это значение, а Service
будет относиться к компонентам Android.
Сравнение Андромеды с другими решениями для взаимодействия компонентов:
Легкость использования | Инвазивность кода | Совместимость | Поддержка IPC | Поддержка событийной шины между процессами | Поддержка перехода между страницами | |
---|---|---|---|---|---|---|
Андромеда | Хорошо | Небольшая | Хорошо | Да | Да | Нет |
DDComponentForAndroid | Плохо | Большая | Плохо | Нет | Нет | Да |
ARouter | Хорошо | Большая | Плохо | Нет | Нет | Да |
Первоначально добавьте classpath
в buildscript
(используйте последнюю версию вместо $version$
):
classpath "org.qiyi.video.svg:plugin:$version"
Это пути к основному коду и плагину Gradle соответственно.
Используйте основной пакет в Application
или модуле библиотеки:
implementation "org.qiyi.video.svg:core:$version"
Используйте плагин Gradle в модуле application:
apply plugin: 'org.qiyi.svg.plugin'
Поскольку Dispatcher управляет всеми данными о процессах, он должен работать в процессе с наибольшим временем жизни.
Если не указано иначе, Dispatcher работает в основном процессе.
Однако, если основной процесс не является самым долгоживущим (например, в музыкальном приложении процесс воспроизведения может жить дольше), следует настроить имя процесса в build.gradle
модуля application, как показано ниже:
dispatcher{
process ":downloader"
}
Здесь ":downloader"
— самый долгоживущий процесс.
Лучше всего выполнить инициализацию в своём Application
(у каждого процесса свой экземпляр Андромеды):
Andromeda.init(Context);
Для определения и реализации локального интерфейса нет существенной разницы со стандартными интерфейсами, за исключением двух моментов:
Регистрация локальных сервисов возможна двумя способами:
Andromeda.registerLocalService(ICheckApple.class.getCanonicalName(), new CheckApple());
Andromeda.registerLocalService(ICheckApple.class, new CheckApple());
Можно использовать следующий метод регистрации, но этот подход крайне нежелателен из-за проблем с переименованием классов:
Andromeda.registerLocalService("wang.imallen.blog.moduleexportlib.apple.ICheckApple", CheckAppleImpl.getInstance());
После регистрации любой модуль в том же процессе может обращаться к этому сервису. Получение сервиса возможно двумя способами:
ICheckApple checkApple = (ICheckApple) Andromeda.getLocalService(ICheckApple.class);
ICheckApple checkApple = (ICheckApple) Andromeda.getLocalService(ICheckApple.class.getCanonicalName());
Не рекомендуется использовать следующий метод получения сервиса, так как это требует согласованности ключей между сторонами:
ICheckApple checkApple = (ICheckApple) Andromeda.getLocalService("wang.imallen.blog.moduleexportlib.apple.ICheckApple");
Дополнительные примеры использования можно найти в активити LocalServiceDemo
модуля app
.
Для длительных операций интерфейсы должны определять Callback
-интерфейсы, и вызывающая сторона должна передавать объект Callback
при вызове интерфейса.
Регистрация и использование удаленных сервисов немного сложнее, поскольку требуется определение aidl
-интерфейсов.
Определите aidl
-интерфейс и выведите сгенерированные Stub
и Proxy
классы для доступности всем модулям. Например, определите интерфейс покупки яблок:
package wang.imallen.blog.moduleexportlib.apple;
import org.qiyi.video.svg.IPCCallback;
interface IBuyApple {
int buyAppleInShop(int userId);
void buyAppleOnNet(int userId, IPCCallback callback);
}
```Реализация интерфейса:
```java
public class BuyAppleImpl extends IBuyApple.Stub {
private static BuyAppleImpl instance;
public static BuyAppleImpl getInstance() {
if (instance == null) {
synchronized (BuyAppleImpl.class) {
if (instance == null) {
instance = new BuyAppleImpl();
}
}
}
return instance;
}
private BuyAppleImpl() {}
@Override
public int buyAppleInShop(int userId) throws RemoteException {
...
}
@Override
public void buyAppleOnNet(int userId, IPCCallback callback) throws RemoteException {
...
}
}
Регистрация удаленных сервисов отличается от регистрации локальных сервисов. Она выполняется через IBinder часть Stub класса, и регистрация возможна через класс интерфейса или его полное имя:
Andromeda.registerRemoteService(IBuyApple.class, BuyAppleImpl.getInstance().asBinder());
или
Andromeda.registerRemoteService(IBuyApple.class.getCanonicalName(), BuyAppleImpl.getInstance().asBinder());
Пример использования в FragmentActivity:
IBinder binder = Andromeda.with(this).getRemoteService(IBuyApple.class);
if (binder == null) {
return;
}
IBuyApple buyApple = IBuyApple.Stub.asInterface(binder);
if (buyApple == null) {
return;
}
try {
buyApple.buyAppleInShop(29);
} catch (RemoteException ex) {
ex.printStackTrace();
}
Примеры использования в других контекстах можно найти в модуле app.
Важно отметить, что удаленный сервис может использоваться как в других процессах, так и в одном процессе, когда используются одинаковые процессы, вызов автоматически понижается до обычного вызова интерфейса внутри процесса, и Binder автоматически обрабатывает это.
Учитывая, что удалённые сервисы также могут иметь долгие операции, требуется поддержка функции обратного вызова (callback) для удалённых вызовов. Для удалённых сервисов, имеющих долгие операции, при определении интерфейсов следует использовать IPCCallback из библиотеки, как показано ниже:
interface IBuyApple {
int buyAppleInShop(int userId);
void buyAppleOnNet(int userId, IPCCallback callback);
}
Метод buyAppleOnNet()
является долгой операцией, поэтому в его определении используется IPCCallback. Важно отметить, что здесь не может использоваться произвольно определённый интерфейс Callback, так как это приведёт к ошибкам компиляции AIDL.
IPCCallback сам представляет собой интерфейс AIDL, который выглядит следующим образом:
interface IPCCallback {
void onSuccess(Bundle result);
void onFail(String reason);
}
Вызовы IPCCallback можно реализовать путём создания объекта, который наследует IPCCallback.Stub и реализует соответствующие методы, как показано ниже:
IBinder buyAppleBinder = Andromeda.getRemoteService(IBuyApple.class);
if (null == buyAppleBinder) {
return;
}
IBuyApple buyApple = IBuyApple.Stub.asInterface(buyAppleBinder);
if (null != buyApple) {
try {
buyApple.buyAppleOnNet(10, new IPCCallback.Stub() {
@Override
public void onSuccess(Bundle result) throws RemoteException {
...
}
@Override
public void onFail(String reason) throws RemoteException {
...
}
});
} catch (RemoteException ex) {
ex.printStackTrace();
}
}
Однако, поскольку вызовы Callback происходят в потоке Binder, а большинство вызывающих сторон хотят, чтобы эти вызовы осуществлялись в главном потоке, библиотека предоставляет базовый класс BaseCallback для использования сторонними разработчиками, как показано ниже:
IBinder buyAppleBinder = Andromeda.getRemoteService(IBuyApple.class);
if (null == buyAppleBinder) {
return;
}
IBuyApple buyApple = IBuyApple.Stub.asInterface(buyAppleBinder);
if (null != buyApple) {
try {
buyApple.buyAppleOnNet(10, new BaseCallback() {
@Override
public void onSucceed(Bundle result) {
...
}
@Override
public void onFailed(String reason) {
...
}
});
} catch (RemoteException ex) {
ex.printStackTrace();
}
}
Второй способ — тот, который мы рекомендуем использовать!
Подробную информацию можно найти в BananaActivity модуля applemodule.
При работе с IPC для повышения приоритета процесса, используемого для получения удалённого сервиса, выполняется bindService(). Так как была выполнена привязка, то естественно должна быть выполнена отвязка для освобождения соединения. На данный момент существует два случая.
public static void unbind(Class<?> serviceClass);
public static void unbind(Set<Class<?>> serviceClasses);
Если был получен один удалённый сервис, следует использовать первый метод unbind(); если несколько, второй.
Событие в Andromeda определяется следующим образом:
public class Event implements Parcelable {
private String name;
private Bundle data;
...
}
То есть событие состоит из имени и данных, данные хранятся в Bundle при передаче сообщений. Имя должно быть уникальным во всём проекте, иначе возможны ошибки. Все данные должны быть помещены в Bundle для возможности передачи между процессами.
Подписка на события проста. Сначала необходимо создать объект, реализующий интерфейс EventListener. Затем можно подписаться на интересующее событие, как показано ниже:
Andromeda.subscribe(EventConstants.APPLE_EVENT, MainActivity.this);
Где MainActivity реализует интерфейс EventListener, и данная строка указывает на подписку на событие с именем EventConstants.APPLE_EVENT.### Публикация событий
Публикация событий проста; достаточно вызвать метод publish
, как показано ниже:
Bundle bundle = new Bundle();
bundle.putString("Result", "gave you five apples!");
Andromeda.publish(new Event(EventConstants.APPLE_EVENT, bundle));
BSD-3-Clause. Подробнее см. файл BSD-3-Clause.
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )