Создавайте разные экземпляры модели представления внутри одного действия.

После недавнего перехода с Dagger на Hilt я начал наблюдать очень странное поведение в отношении ViewModels. Ниже приведен фрагмент кода:


@HiltAndroidApp
class AndroidApplication : Application() {}

@Singleton
class HomeViewModel @ViewModelInject constructor() :
    ViewModel() {}

@AndroidEntryPoint
class HomeFragment : Fragment(R.layout.fragment_home) {

    private val homeViewModel by viewModels<HomeViewModel>()

    override fun onResume() {
        super.onResume()
        Timber.i("hashCode: ${homeViewModel.hashCode()}")
    }
}


@AndroidEntryPoint
class SomeOtherFragment : Fragment(R.layout.fragment_home) {

    private val homeViewModel by viewModels<HomeViewModel>()

    override fun onResume() {
        super.onResume()
        Timber.i("hashCode: ${homeViewModel.hashCode()}")
    }
}

Значение hashCode не согласовано во всех фрагментах. Я не могу понять, что еще мне не хватает для создания одноэлементного экземпляра модели представления в рамках действия.

Я использую дизайн единого действия и добавил все необходимые зависимости.


person ritesh4302    schedule 24.06.2020    source источник
comment
Не комментируйте свою модель просмотра с помощью @Singleton.   -  person EpicPandaForce    schedule 24.06.2020
comment
Почему вы аннотируете свою модель представления с помощью @Singleton?   -  person Amjad Alwareh    schedule 27.06.2020
comment
Да, я удалил его.   -  person ritesh4302    schedule 27.06.2020


Ответы (2)


Когда вы используете by viewModels, вы создаете ViewModel, привязанную к этому отдельному фрагменту - это означает, что каждый фрагмент будет иметь свой собственный индивидуальный экземпляр этого класса ViewModel. Если вы хотите, чтобы один экземпляр ViewModel охватил все действие, вам нужно использовать _ 2_

private val homeViewModel by activityViewModels<HomeViewModel>()
person ianhanniballake    schedule 24.06.2020
comment
это работает, val actViewModel: ‹ActivityViewModel› по activityViewModels () - person d-feverx; 13.12.2020

То, что Ян говорит, правильно, by viewModels - это функция расширения фрагмента, и он будет использовать фрагмент как ViewModelStoreOwner.

Если вам нужно, чтобы он был привязан к Activity, вы можете использовать by activityViewModels.

Однако обычно вам не нужны ViewModels с областью действия. Они фактически глобальны в приложении с одним действием.

Чтобы создать глобальный компонент без отслеживания состояния, вы можете использовать @ActivityRetainedScope в Hilt. Они будут доступны для ваших ViewModels, созданных в Activity или Fragment.

Чтобы создать компоненты с сохранением состояния, вы должны полагаться на @ViewModelInject и @Assisted, чтобы получить SavedStateHandle.

Существует высокая вероятность того, что в этот момент вместо ViewModel с областью действия вы действительно захотите ViewModel с областью действия NavGraph.

Чтобы получить SavedStateHandle в ViewModel с областью видимости NavGraph внутри фрагмента с помощью аннотации @Assisted Hilt, вы можете (EDIT: cannot) использовать:

//@Deprecated
//inline fun <reified T : ViewModel> Fragment.hiltNavGraphViewModels(@IdRes navGraphIdRes: Int) =
//viewModels<T>(
//    ownerProducer = { findNavController().getBackStackEntry(navGraphIdRes) },
//    factoryProducer = { defaultViewModelProviderFactory }
//)

.

РЕДАКТИРОВАТЬ: из-за https://github.com/google/dagger/issues/2152 этот подход не работает, поэтому может работать только использование аксессоров и построение AbstractSavedStateViewModelFactory в области видимости NavGraph напрямую с помощью аксессоров. На данный момент это немного беспорядочно, потому что к ActivityRetainedComponent трудно получить доступ, поэтому следите за обновлениями, чтобы найти лучшее решение ...

person EpicPandaForce    schedule 24.06.2020
comment
Обратите внимание, что объект @ActivityRetainedScope хранится в модели ViewModel с областью действия, поэтому время жизни этого объекта и время жизни самой модели ViewModel с областью действия будет точно таким же. - person ianhanniballake; 24.06.2020
comment
Это правда, и именно поэтому я сказал, что он, как правило, не должен иметь состояния, или состояние в нем должно быть временным (поскольку вы не можете видеть SavedStateHandle, который будет принадлежать охватывающему SavedStateHandle). Идея заключалась в том, что если вы будете использовать виртуальную машину с областью действия и вам не нужен SavedStateHandle, то вы можете сделать это с помощью Hilt без необходимости использовать виртуальную машину в качестве держателя (или расширить VM). . - person EpicPandaForce; 25.06.2020