Может ли статика помочь сохранить Dagger компонент при смене конфигурации и какие риски с этим связаны
Что проверяют
Понимаете ли вы, чем жизненный цикл процесса отличается от жизненного цикла Activity, и где на самом деле должен жить граф зависимостей. Следом обычно спрашивают, что будет с таким компонентом после смерти процесса и как ту же задачу решает Hilt.
Как отвечать
Технически да. Статическое поле живёт, пока жив процесс, а поворот экрана пересоздаёт Activity, но не процесс. Поэтому компонент в companion object или в синглтоне-держателе переживёт смену конфигурации. AppComponent так и держат, в поле Application.
Проблемы начинаются с компонентами короче приложения:
- утечка Activity: если в компонент через
@BindsInstanceпередана Activity или что-то, что держит на неё ссылку (адаптер,View,Contextэкрана), статика удержит уничтоженную Activity со всей иерархиейView; - смерть процесса: система убивает фоновый процесс, статика обнуляется, а Activity восстанавливается из
savedInstanceState. Код, ожидавший готовый компонент, падает; - очистка: компонент нужно обнулять при настоящем закрытии экрана, в
onDestroyприisFinishingи!isChangingConfigurations. Забыли, и он живёт до конца процесса; - несколько экземпляров: два одинаковых экрана в стеке получат один общий компонент и общее состояние;
- тесты: состояние переезжает из теста в тест, результат зависит от порядка запуска.
Правильная замена: хранить компонент экрана во ViewModel, она переживает смену конфигурации и сама сообщает о конце жизни через onCleared(). В Hilt это уже встроено: ActivityRetainedComponent со скоупом @ActivityRetainedScoped живёт между поворотами, а ViewModelComponent привязан к ViewModel. Всё, что зависит от Activity, в такой компонент не кладут.
Где спотыкаются
Советуют WeakReference против утечки. Это прячет проблему: объект может исчезнуть посреди работы.
Путают SavedStateHandle с хранилищем объектов. Он рассчитан на небольшие сериализуемые данные, граф зависимостей туда не положить.
Проверяют только поворот экрана и забывают про смерть процесса. Воспроизвести её легко: свернуть приложение, выполнить adb shell am kill <пакет> и вернуться в него.