Комментарии 7
Спасибо за статью, как всегда очень основательно.
Вы не рассматривали возможность использовать мультибиндинги из какого-нибудь DI фреймворка или Manual DI? App ведь знает о всех модулях и может строить общий DI граф в котором может быть какая-то коллекция интерфейсов нужного типа.
Мультибиндинги рассматривали. Более того, до внедрения описанной в статье системы с meta-data, мультибиндинги были основным способом создания коллекций таких интерфейсов. Они и сейчас у нас остаются для, скажем так, не критичных штук - вроде диплинков и шорткатов.
В целом это весьма хорошее решение. Но конкретно у нас с таким подходом есть ряд проблем:
Нет прямой поддержки dynamic-feature, так как их код не виден из главного модуля.
Для демоприложений такой граф надо настраивать для каждого из демоприложений. Потом ещё и поддерживать. С системой с meta-data достаточно просто подключить модуль к приложению и он сам заработает.
У app есть два варианта "знать" о подключённых к нему модулях: implementation и runtimeOnly. Первый вариант позволяет видеть код из подключённых модулей в app, но в тоже время, если в каком-то из подключённых модулей меняется код, то и app вынужден пересобраться (иногда лишь частично, но не суть). Это било по времени горячей сборки и мы перешли на второй вариант - runtimeOnly. С ним код в app уже не доступен, зато и лишних пересборок нет. Так как код недоступен, то и от мультибиндингов смысла нет.
Текст поднимает важную тему организации кода в мобильных приложениях и показывает, как использование глобальных событий может помочь в решении проблемы перегруженности и сложности поддержки кода. Разбирая примеры использования, автор стимулирует читателя обратить внимание на необходимость эффективной архитектуры приложений.
Есть ещё библиотека colonist от joom.
https://github.com/joomcode/colonist
Проблема сбора классов там решается патчингом байткода. Не берусь утверждать лучше это или хуже мета тегов в манифесте, но однозначно стоит обратить внимание.
Спасибо за статью, у меня 2 вопроса
1. почему бы вместо private val receivers = mutableListOf<GlobalEventReceiver>()
взять SharedFlow ?
2. с многомодульной архитектурой все ивенты должны лежать в каком-то core модуле от которого должны зависить все остальные модули и каждый новый ивент влияет на сборку всех модулей. Как это победить ?
1) С ходу профита особого не вижу. Это просто property в которую идёт запись и чтение. Но если есть сценарий, где SharedFlow что-то сильно улучшит, то можно без проблем и его применить.
2) В таком случае можно GlobalEvent сделать не sealed class, а interface. Реализации хранить в нескольких модулях (например, api-модуль фичей), которые подключать к тем модулям, которым этот GlobalEvent нужен. Правда тут стоит быть аккуратным, если GlobalEvent нужен лишь ограниченному числу модулей, то возможно он не такой уж и глобальный? И стоит рассмотреть другие варианты общения между компонентами.
Спасибо за статью, весьма обширное количество примеров с минусами / плюсами / оптимизации.
У нас в приложении решили пойти немного другим путем, по доставке какого то события к какому то компоненту - это создание некого «почтового ящика», который позволяет записать событие условно в строку в БД и передать событие в интересующий модуль. Очень удобно и довольно не проблематично .
Глобальные события в мобильном приложении