Под стейтом приложения - имею ввиду, что демо фичи A может содержать только экран с фичей A.
А показа этого экрана может требоваться какое-то предварительно сформированное состояние: например, при переходе на экран оплаты заказа, юзер должен быть авторизован, а где-то в хранилище должна лежать сформированная корзина.
Еще один интересный момент - это как формировать (и стоит ли) работу с реальным/тестовым сервером в демо-аппках, и стоит ли давать такую возможность.
Кстати, по поводу DI у нас очень хорошо себя показывает Anvil: он минимизирует конфигурацию в демках, и убирает большое количество бойлерлпейта. Интересно было бы послушать и про ваше решение)
Похожая схема как раз описывается в докладе Square, и там называется fake-модулями.
У каждой фичи есть feature:public-часть, которая содержит только публичные интерфейсы. Реальные имплементации этих интерфейсов находятся в feature:impl, а фейковые - в feature:fake:
С такой структурой модулей настраивать различные степени валидации и проверки для демо-приложений. Например, если для критична атомарность - каждое демо-приложение может подключить в себя только одну реальную фичу, а все остальные реализации public-модулей должны быть фейками.
В Avito мы в данный момент адоптим этот подход, и переходим на структуру модулей с public/impl/fake, одновременно выправляя зависимости демок.
Спасибо больше за статью, в русскоязычном сегменте не так много материалов по тому, как правильно готовить демки. Круто, что вы решили его подготовить.
Мне в статье не хватило конкретных проблем и историй о том, как вы их решали и поддерживали корректность работы. Очень часто в демо-приложениях "болит" конфигурация DI, бойлерплейт, некорректное подключение gradle-зависимостей, формирование корректного стейта приложения для конкретного экрана, демо-аппы для флоу vs атомарные демо-аппы для фич, Было бы круто это так же описать и рассказать. По моему опыту, это не только проблемы вашего проекта, они возникают у всех, кто реализует этот подход)
Мы в Avito начали делать демо-приложения еще 3 года назад, и столкнулись с многими проблемами, которые вы описали: heavy-модули, тянущие за собой огромное количество реализаций, транзитивное подключение цепочек модулей, и так далее.
Square в своих докладах по модуляризации предлагает отличную архитектуру для того, чтобы рулить сложностью демо-приложений . Там рассказывается довольно простой и понятный концепт инверсии зависимостей, только прямо на уровне билд-системы.
Еще интересные инсайты по тому, как организовать многомодульный проект и следить за корректностью связей рассказывают Tinder в своем докладе по развязыванию структуры модулей. При работе с демками очень важна валидация корректной структуры зависимостей, потому что если они будут
Мы в Surf когда-то писали анализатор kotlin-файлов на основе Gradle, который валидировал, что экран, который указан в точке входа в фичу, существует в проекте (не спрашивайте, почему это было сделано не через линт).
Я подумал, что подобный код вполне можно было бы переиспользовать для генерации экспериментов (строим дерево файлов проекта и анализируем эксперименты), но, кажется, это довольно плохо скейлится.
Плюс, есть более оптимальные решения (тот же kapt/ksp/compiler plugin), позволяющие делать аналогичные действия.
Я кстати для похожей задачи решил использовать ksp. Мне очень понравилось, потому что получилось обойтись без рефлексии и build time тоже почти не пострадал. Вначале тоже хотели использовать ClassIndex, но в нашем случае необходимо было использовать в код, собирающий информацию об экспериментах в проде, и это рождало несколько проблем:
Генерация всех экспериментов в Meta-Inf файл - это неприятная дыра в безопасности, как уже было отмечено в статье
При обращении к ServiceLoader.load, он считывает и инстанциирует все эксперименты из файла, а если этот файл достаточно большой (например, несколько сотен экспериментов) - это может сильно сказаться на производительности.
Спасибо за статью, описан достаточно зрелый подход и ресерч мне понравился.
Только с генерацией экспов - немного не хватило ещё одного приема: codegen'a с помощью Gradle.
У этого подхода есть один плюс: мы можем вручную добавить коллекцию в build директорию проекта, и увидеть все эксперименты в проекте без танцев с бубном, необходимости запускать дебажное приложение и вносить что-то руками.
Спасибо за статью! Идея с контейнером-шаред элементом классная, но есть пара пара замечаний по коду внутри:
Не советую использовать зашитые в Android константы для определения размера статус-бара. Это плохая практика, и на различных девайсах от Meizu, Dogee, и прочих нонеймах с кастомными надстройками работать не будет. Советую работать через windowInsets, статей по этому подходу достаточно много, например здесь: https://habr.com/ru/company/surfstudio/blog/464373/
В методе getScreenHeight используется только статус бар, но при этом мы забываем про высоту навигейшн-бара, из-за чего на девайсах с нижним баром навигации, контейнеры улетят вверх.
Высоту и ширину экрана можно получить гораздо проще, без необходимости прибегать к системным сервисам: view.resources.displayMetrics содержит нужную инфу.
C Landscape будет немного посложнее, как раз из-за того, что navbar может быть слева/справа, и при этом снизу на планшетах может выдвинуться клавиатура.
Чтобы пример работал корректно с такими входными данными, нужно добавить недостающие поля в listener:
typealias OnSystemInsetsChangedListener = (
statusBarSize: Int,
bottomNavigationBarSize: Int,
leftNavigationBarSize: Int,
rightNavigationBarSize: Int
) -> Unit
Добавить эти параметры в вызов listener'а:
listener(topInset, if (hasKeyboard) 0 else bottomInset, leftInset, rightInset)
Решения «из коробки» нет, но так как под капотом RecyclerView, и в качестве адаптера у пейджера можно использовать RecyclerView.Adapter, можно ворох уже готовых инструментов для упрощения работы с ним. Например, уже упомянутый в статье EasyAdapter имеет эту функцию, и включается она простым вызовом метода:
adapter.setInfiniteScroll(true)
Пример работы со списком с RecyclerView.Adapter и динамическим списком с эмуляцией пагинации можете найти в этой ветке семпла.
Оба пункта скорее относятся не к превосходству компонента над предшественником, а об удобности его использования в принципе.
setOffscreenPageLimit в ViewPager действительно был, но его не было у RecyclerView. Точнее, похожий эффект может быть достигнут с помощью довольно сложных манипуляций с LayoutManager'ом, что и сделали разработчики VP2.
Под стейтом приложения - имею ввиду, что демо фичи A может содержать только экран с фичей A.
А показа этого экрана может требоваться какое-то предварительно сформированное состояние: например, при переходе на экран оплаты заказа, юзер должен быть авторизован, а где-то в хранилище должна лежать сформированная корзина.
Еще один интересный момент - это как формировать (и стоит ли) работу с реальным/тестовым сервером в демо-аппках, и стоит ли давать такую возможность.
Кстати, по поводу DI у нас очень хорошо себя показывает Anvil: он минимизирует конфигурацию в демках, и убирает большое количество бойлерлпейта. Интересно было бы послушать и про ваше решение)
Похожая схема как раз описывается в докладе Square, и там называется fake-модулями.
У каждой фичи есть
feature:public
-часть, которая содержит только публичные интерфейсы. Реальные имплементации этих интерфейсов находятся вfeature:impl
, а фейковые - вfeature:fake
:С такой структурой модулей настраивать различные степени валидации и проверки для демо-приложений. Например, если для критична атомарность - каждое демо-приложение может подключить в себя только одну реальную фичу, а все остальные реализации public-модулей должны быть фейками.
В Avito мы в данный момент адоптим этот подход, и переходим на структуру модулей с public/impl/fake, одновременно выправляя зависимости демок.
Спасибо больше за статью, в русскоязычном сегменте не так много материалов по тому, как правильно готовить демки. Круто, что вы решили его подготовить.
Мне в статье не хватило конкретных проблем и историй о том, как вы их решали и поддерживали корректность работы. Очень часто в демо-приложениях "болит" конфигурация DI, бойлерплейт, некорректное подключение gradle-зависимостей, формирование корректного стейта приложения для конкретного экрана, демо-аппы для флоу vs атомарные демо-аппы для фич, Было бы круто это так же описать и рассказать. По моему опыту, это не только проблемы вашего проекта, они возникают у всех, кто реализует этот подход)
Мы в Avito начали делать демо-приложения еще 3 года назад, и столкнулись с многими проблемами, которые вы описали: heavy-модули, тянущие за собой огромное количество реализаций, транзитивное подключение цепочек модулей, и так далее.
Square в своих докладах по модуляризации предлагает отличную архитектуру для того, чтобы рулить сложностью демо-приложений . Там рассказывается довольно простой и понятный концепт инверсии зависимостей, только прямо на уровне билд-системы.
Еще интересные инсайты по тому, как организовать многомодульный проект и следить за корректностью связей рассказывают Tinder в своем докладе по развязыванию структуры модулей. При работе с демками очень важна валидация корректной структуры зависимостей, потому что если они будут
Привет! Извиняюсь за поздний ответ)
Мы в Surf когда-то писали анализатор kotlin-файлов на основе Gradle, который валидировал, что экран, который указан в точке входа в фичу, существует в проекте (не спрашивайте, почему это было сделано не через линт).
Я подумал, что подобный код вполне можно было бы переиспользовать для генерации экспериментов (строим дерево файлов проекта и анализируем эксперименты), но, кажется, это довольно плохо скейлится.
Плюс, есть более оптимальные решения (тот же kapt/ksp/compiler plugin), позволяющие делать аналогичные действия.
Я кстати для похожей задачи решил использовать ksp. Мне очень понравилось, потому что получилось обойтись без рефлексии и build time тоже почти не пострадал. Вначале тоже хотели использовать ClassIndex, но в нашем случае необходимо было использовать в код, собирающий информацию об экспериментах в проде, и это рождало несколько проблем:
Генерация всех экспериментов в Meta-Inf файл - это неприятная дыра в безопасности, как уже было отмечено в статье
При обращении к ServiceLoader.load, он считывает и инстанциирует все эксперименты из файла, а если этот файл достаточно большой (например, несколько сотен экспериментов) - это может сильно сказаться на производительности.
Спасибо за статью, описан достаточно зрелый подход и ресерч мне понравился.
Только с генерацией экспов - немного не хватило ещё одного приема: codegen'a с помощью Gradle.
У этого подхода есть один плюс: мы можем вручную добавить коллекцию в build директорию проекта, и увидеть все эксперименты в проекте без танцев с бубном, необходимости запускать дебажное приложение и вносить что-то руками.
Спасибо за статью! Идея с контейнером-шаред элементом классная, но есть пара пара замечаний по коду внутри:
Чтобы пример работал корректно с такими входными данными, нужно добавить недостающие поля в listener:
Добавить эти параметры в вызов listener'а:
И реагировать на них в Activity:
Таким образом, мы будем корректно обрабатывать все кейсы поворота экрана и выезжающую снизу клавиатуру.
Пример работы со списком с RecyclerView.Adapter и динамическим списком с эмуляцией пагинации можете найти в этой ветке семпла.
setOffscreenPageLimit в ViewPager действительно был, но его не было у RecyclerView. Точнее, похожий эффект может быть достигнут с помощью довольно сложных манипуляций с LayoutManager'ом, что и сделали разработчики VP2.