Должен ли я использовать ViewModelScope независимо от жизненного цикла фрагмента?

#android #android-fragments #kotlin-coroutines #android-viewmodel

Вопрос:

TL;DR

Для одного экземпляра фрагмента, который добавляется/удаляется снова и снова, viewModelScope в его ViewModel работает только до тех пор, пока фрагмент не будет извлечен в первый раз. После этого viewModelScope становится неактивным. Как я могу повторно инициализировать viewModelScope , когда я добавляю фрагмент обратно? Или мне следует поискать лучшую реализацию, чтобы она оставалась активной?

Сценарий:

Я использовал RxJava through ViewModel для извлечения данных из сети в старом проекте и для отправки обновлений в пользовательский интерфейс, я использовал LiveData.

Ниже показано, как я инициализировал типичную модель представления внутри фрагмента:

 private val fruitsViewModel by viewModels<FruitsViewModel>()
 

Ниже приведена вышеупомянутая модель представления:

 class FruitsViewModel: ViewModel {

    private var category: String = FruitCategories.DEFAULT

    private val _fruits = MutableLiveData<List<Fruit>>()
    fun fruits(): LiveData<List<Fruit>> = _fruits

    fun updateCategory(newCategory: String) {
        this.category = newCategory
        fetchFruits()
    }

    fun fetchFruits() { ... }
}
 

В «Фрагменте onViewCreated() » я позвонил fruitsViewModel.fetchFruits() . Это отлично сработало(без каких-либо видимых проблем).

Сегодня я переключил конкретный код RxJava с сопрограммами для извлечения сетевых данных в моей модели просмотра. Что-то вроде этого:

 fun fetchFruits() {

    /* this.fruitsJob = */ 
    viewModelScope.launch {
        ...
    }
}
 

Это тоже хорошо работает, даже после того, как я заменяю фрагмент и возвращаюсь к нему. Но, если я сохраню экземпляр фрагмента, объявленный в действии, и буду использовать его снова, viewModelScope.launch {} он больше не будет работать. Что-то вроде этого:

 class FruitsActivity: AppCompatActivity() {

    private val fruitsFragment = FruitsFragment()

    private fun showFruitsFragment() {
        supportFragmentManager
            .beginTransaction()
            .replace(getFragmentContainerId(), fruitsFragment)
            .addToBackStack(fruitsFragment.tag)
            .commit()
    }

    private fun removeFragments() {
        val manager = supportFragmentManager
        if (manager.backStackEntryCount > 0) {

            val first: FragmentManager.BackStackEntry = manager.getBackStackEntryAt(0)
            manager.popBackStack(first.id, FragmentManager.POP_BACK_STACK_INCLUSIVE)
        }
    }
} 
 

Причина сохранения экземпляра фрагмента заключается в том, чтобы загрузить данные API только один раз, и после этого они должны просто появляться по нажатию кнопки без какого-либо мерцания/загрузки.

После того , как я позвоню removeFragments() , fruitsViewModel уже позвонила ее onCleared() для сохраненной декларации fruitsFragment . Таким образом, он viewModelScope становится неактивным, и если я fruitsFragment снова добавлю его , позвонив showFruitsFragment() , это больше не будет извлекать сетевые данные. Это не имело отношения к моей реализации Rxjava, потому что я ее не использовал viewModelScope .

Я думаю, что могу просто решить проблему, связавшись fruitsViewModel FruitsActivity вместо этого с экземпляром. Но я хочу знать, можно ли считать это лучшим способом.

Комментарии:

1. The reason for keeping the fragment instance is to load API data only once итак, похоже, вам нужна модель общего представления, не так ли ? почему бы не прикрепить модель представления к действию и не заставить ее выполнять вызовы служб по мере необходимости

2. Согласен с @a_local_nobody . Поскольку вы сохраняете фрагмент дольше (аналогично сроку действия), независимо от того, сколько раз он присоединялся и отсоединялся, лучше использовать область действия или модель представления действия.

3. Кстати, поскольку вы хотите сохранить загруженные данные независимо от жизненного цикла фрагмента, данные должны храниться в действии, а не во фрагменте. ИМО, хранение экземпляров фрагментов не является хорошей практикой.

Ответ №1:

Да, вы должны viewModel по-другому оценивать свои возможности, в вашем случае определение области fruitsViewModel FruitsActivity имеет полный смысл.

Я не уверен, как RxJava работал ваш код, не видя кода, я предполагаю, что это как-то связано с тем disposed , что он был неправильным.

Кроме того, даже если вы создадите область, которая переживет viewModelScope (например, создание и поддержание a CoroutineScope в деятельности), моя главная проблема заключается в том, что подход не является интуитивным/естественным, людям будет сложнее проверить, как и где это CoroutineScope поддерживается

Комментарии:

1. Согласен, спасибо. p.s. реализация, связанная с RxJava, утилизировала все расходные onCleared() материалы методом ViewModel. Это позволяет создавать новые вызовы после onCleared (), поскольку RxJava не связана ни с каким жизненным циклом. Но для сопрограмм я зависим от viewModelScope того, что связано с жизненным циклом(?).

2. Хм, интересно, чем отличается реализация, потому что технически viewModelScope отменяет сопрограмму в onCleared()

3. Реализация RxJava не зависела от сопрограмм или viewModelScope . fetchFruits() это было так же, как метод в любом другом классе.