Ранние версии библиотеки полагались на механизм
setRetainInstanceState(), реализация которого подробно описана в
статье. На сегодняшний день
setRetainInstanceState() больше не используется
Здесь говорим о библиотеках
fragment-ktx версии
1.2.4 и
lifecycle-viewmodel-ktx версии
2.2.0.
Реализация библиотек может быть изменена в будущих версиях.Для начала разберемся как создается
ViewModel:
1. ViewModel рекомендуется создавать через вызов
Delegated Property by viewModel, который принимает две функции:
ownerProducer: () -> ViewModelStoreOwner и
factoryProducer: (() -> Factory)?. По-умолчанию
ownerProducer – это функция, возвращающая
this, где
this – это фрагмент, на котором вызван
by viewModel.
2. by viewModel создает и возвращает объект типа
ViewModelLazy. При создании этого объекта одним из параметров передается функция
storeProducer: () -> ViewModelStore. Эта функция создается следующим образом:
{ ownerProducer().viewModelStore }, где
ownerProducer – функция, заданная на предыдущем шаге.
3. ViewModelLazy имеет один метод
get(), который вызывается при обращении к property, заданной через
by viewModel. При первом вызове метода
get() создается объект
ViewModel и сохраняется в классе
ViewModelLazy. При последующих вызовах
get() возвращается уже созданный
ViewModel.
4. Для создания
ViewModel сначала создается объект
ViewModelProvider, после этого на нем вызывается
get() c классом модели в качестве параметра:
ViewModelProvider(store, factory).get(viewModelClass.java)5. Метод
ViewModelProvider.get() создает объект
ViewModel с помощью переданной
Factory и сохраняет его во
ViewModelStore, который хранит значения в
HashMap. При последующих вызовах метода
ViewModelProvider.get(),
ViewModel достается из
ViewModelStore.
При описанной реализации граф ссылок на объект
ViewModel во фрагменте выглядит так:
Fragment -> ViewModelLazy -> ViewModelProvider -> ViewModelStore -> ViewModel
Т.е. если
Fragment уничтожен, то
ViewModel тоже будет удалена.
Как же
ViewModel переживает пересоздание фрагмента? Ответ кроется во
ViewModelStoreOwner, который использовался в
by viewModel на первом шаге.
В следующем посте мы разберем как и где сохраняется
ViewModelStoreOwner.
В
прошлом посте мы разобрали как создается
ViewModel. Теперь рассмотрим как
ViewModelStoreOwner переживает пересоздание фрагмента.
Как было упомянуто ранее,
by viewModel одним из аргументов принимает функцию
ownerProducer: () -> ViewModelStoreOwner. По умолчанию используется функция, возвращающая текущий фрагмент:
ownerProducer = { this }.
Класс
Fragment из Jetpack реализует интерфейс
ViewModelStoreOwner, который имеет только один метод
getViewModelStore(): ViewModelStore.
Но сам
Fragment не хранит объект
ViewModelStore, а делегирует вызов во
FragmentManager:
mFragmentManager.getViewModelStore(this), где this – текущий фрагмент
FragmentManager, в свою очередь, делегирует вызов в класс
FragmentManagerViewModel. Этот класс хранит
HashMap, в котором ключами выступают внутренний
uuid фрагмента, а значениями – объекты
ViewModelStore. Если
HashMap не имеет
ViewModelStore для запрашиваемого фрагмента, то создается новый инстанс
ViewModelStore и сохраняется в
HashMap.
Граф ссылок на
ViewModel фрагмента выглядит так:
Fragment (as ViewModelStoreOwner) -> FragmentManager -> FragmentManagerViewModel -> HashMap<String, ViewModelStore> -> ViewModelStore -> ViewModel
FragmentManagerViewModel переживает пересоздание фрагмента, а вместе с ним сохраняется и
ViewModel.
Следующий вопрос: как
FragmentManagerViewModel переживает пересоздание фрагмента?
Трюк заключается в том, что класс
FragmentManagerViewModel – это
ViewModel, и он сохраняется также как и любой другой объект
ViewModel.
Объектом
ViewModelStoreOwner для
FragmentManagerViewModel выступает
FragmentActivity:
Fragment (as ViewModelStoreOwner) -> FragmentManager -> FragmentManagerViewModel -> FragmentActivity (as ViewModelStore) -> …
В следующем посте разберем, как
ViewModel переживает пересоздание активити.