1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/bettar-Andromeda

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
CHINESE_README.md 20 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
Отправлено 03.03.2025 13:57 63a8086

Андромеда

Андромеда_лицензия Андромеда_ядра_версия Андромеда_плагин_версия

Андромеда предоставляет управление взаимодействием компонентов через интерфейсы, включая локальные вызовы внутри одного процесса и межпроцессные вызовы.

Примечание: Разделение на локальные сервисы и удаленные сервисы связано с тем, что интерфейсы локальных сервисов могут передавать различные типы аргументов и возвращаемых значений, тогда как удаленные интерфейсы ограничены 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 управляет всеми данными о процессах, он должен работать в процессе с наибольшим временем жизни. Если не указано иначе, 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());

Использование удаленных сервисов

  • Поскольку Андромеда использует bindService() для повышения приоритета связи, автоматически освобождает связь в onDestroy().
  • Поскольку межпроцессное взаимодействие позволяет передавать только IBinder, необходимо получить IBinder удаленного сервиса и затем использовать XX.Stub.asInterface() для получения его прокси.

Пример использования в 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 для удалённых сервисов

Учитывая, что удалённые сервисы также могут иметь долгие операции, требуется поддержка функции обратного вызова (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(). Так как была выполнена привязка, то естественно должна быть выполнена отвязка для освобождения соединения. На данный момент существует два случая.

  • Для фрагментов или активностей, где вызов происходит в главном потоке, достаточно передать объект Fragment или Activity при получении удалённого сервиса с помощью with(), и Andromeda автоматически выполнит отвязку в методе onDestroy(). Разработчику не придётся выполнять никаких действий для отвязки.
  • Для вызовов в фоновых потоках или вне фрагментов/активностей, необходимо передать контекст приложения для получения удалённого сервиса. После завершения работы необходимо выполнить отвязку с помощью методов Andromeda.unbind():
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.

Поддержка

  1. Примеры кода
  2. Чтение Wiki или FAQ
  3. Обратиться по адресу bettarwang@gmail.com

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/bettar-Andromeda.git
git@api.gitlife.ru:oschina-mirror/bettar-Andromeda.git
oschina-mirror
bettar-Andromeda
bettar-Andromeda
master