Кто работает в команде Мобильной Архитектуры Авито?
Только представьте, сегодня понедельник, по этим дням в Авито проходят community meetings с разработчиками Android и iOS. Меня зовут Михаил. И я Android разработчик в команде Мобильной Архитектуры. Уже через 5 минут я буду выступать перед нашим большим сообществом Android разработчиков и рассказывать про новый подход в построении DI в нашем приложении при помощи Anvil.
Два месяца назад мы провели очередной опрос наших коллег - Android разработчиков из продуктовых команд - с какими трудностями они встречались при разработке своих фичей. Одной из самых больших болей оказалась работа с механизмом Dependency Injection: граф зависимостей был запутанным, разобраться, особенно новичку, крайне сложно. Да и скорость сборки с увеличением количества команд и фичей стала страдать.
На планировании квартала наша команда решила взять задачу оптимизации DI себе в цель и я вызвался лидировать эту задачу.
Прежде всего я обратился к community и на встрече мы обсудили основные проблемы и сложности с текущим DI, который у нас построен на Dagger 2. Я аккуратно записал все проблемы:
Сложно заменять зависимости в тестовом коде и демо-аппах.
Много бойлерплейта.
Непонятен граф зависимостей.
Сложно добавить component dependencies из-за того, что нужно трогать много классов, и IDE тормозит.
Импакт на build-time: kapt-процессинг аннотаций даггера входит в критический путь и занимает большое время.
На этой же встрече мы определили, чего ожидаем от новой системы DI:
Строгие правила по организации зависимостей.
Простое понимание того, как подключить ту или иную зависимость.
Уменьшение количества бойлерплейта при написании DI.
Простота в подмене зависимостей в тестах и в демо-приложениях.
Улучшение скорости сборки.
Из инструментов мы могли использовать только библиотеки на базе Dagger 2 из-за его compile time проверок графа зависимостей и большого количества даггеро-специфичной логики, написанной в проекте. К таким инструментам относятся Hilt и Anvil.
Для тестирования DI-фреймворка я придумал набор сценариев, с которыми чаще всего сталкиваются разработчики, когда пишут код и работают с зависимостями.
Добавление новой зависимости в Application component.
Добавление application-scope зависимости в компонент экрана.
Добавление screen-scope зависимости в компонент экрана.
Мультибиндинг: Добавление зависимости в однотипный список (Set, Map).
Подмена зависимости в тестах.
Подмена зависимости в DemoApp.
Добавление зависимостей в ViewModel.
Далее я провел тесты производительности. Тестирование производительности я производил с помощью Gradle-profiler. Все сценарии имели 2 стадии разогрева и 3 итерации прогона. Для чистоты эксперимента build cache отключен.
В результате оказалось, что Anvil выигрывает (-5-10% времени) по времени чистого билда, и на модулях, в которых не происходили изменения. Однако при инкрементальной сборке на модулях, в которых включен kapt, anvil занимает больше времени, иногда в 3 и более раз. Это происходит из-за того, что anvil не поддерживает инкрементальную генерацию kapt stubs. У этой проблемы есть WorkAround: можно выносить DI-код из большого модуля, это поможет kapt-процессору обрабатывать меньше кода, и в итоге модуль будет обработан значительно быстрее.
Далее я написал тестовый модуль в нашем проекте на Anvil, подготовил отдельно примеры кода и подготовил презентацию для выступления которая как раз сейчас началась! Еще увидимся!
Кстати, еще мы ищем коллегу Android-разработчика в нашу команду. Приходи на собес, будем тебе рады :)