liveData с сопрограммами запускается только в первый раз - PullRequest
0 голосов
/ 13 июля 2020

У меня есть пример использования:

  1. Открыть приложение + отключить сеть -> ошибка отображения
  2. Выйти из приложения, затем включить сеть, затем снова открыть приложение

Ожидается: данные загрузки приложения

Фактически: ошибка отображения приложения, означающая, что ошибка состояния кэшируется, liveData не генерируется

Класс репозитория

    class CategoryRepository(
    private val api: ApiService,
    private val dao: CategoryDao
) {
    val categories: LiveData<Resource<List<Category>>> = liveData {
        emit(Resource.loading(null))

        try {
            val data = api.getCategories().result
            dao.insert(data)

            emit(Resource.success(data))
        } catch (e: Exception) {

            val data = dao.getCategories().value

            if (!data.isNullOrEmpty()) {
                emit(Resource.success(data))
            } else {
                val ex = handleException(e)
                emit(Resource.error(ex, null))
            }
        }
    }
}

Класс ViewModel

class CategoryListViewModel(
    private val repository: CategoryRepository
): ViewModel() {

    val categories = repository.categories
}

Класс фрагмента, где LiveDate obsever

viewModel.apply {
            categories.observe(viewLifecycleOwner, Observer {
                // live data only trigger first time, when exit app then open again, live data not trigger
            })
        }

не могли бы вы помочь мне объяснить, почему живые данные не запускаются в этом сценарии использования и как исправить? Большое спасибо

Обновление

Я решил вышеуказанную проблему, заменив val categories на func categories() в классе репозитория. Однако я не понимаю и не могу объяснить, почему он правильно работает с func, но не с val.

1 Ответ

1 голос
/ 13 июля 2020

Почему это происходит? Это происходит потому, что ваш ViewModel еще не убит. ViewModel на cleared() вызывается, когда Fragment уничтожается. В вашем случае ваше приложение не убивается, и LiveData просто генерирует последнее уже установленное событие. Не думаю, что стоит использовать liveData builder. Просто выполните метод в ViewModel, когда ваш фрагмент попадет в onResume():

override fun onResume(){
  viewModel.checkData()
  super.onResume()
}
// in the viewmodel
fun checkData(){
   _yourMutableLiveData.value = Resource.loading(null)

        try {
            val data = repository.getCategories()
            repository.insert(data)

            _yourMutableLiveData.value = Resource.success(data)
        } catch (e: Exception) {

            val data = repository.getCategories()

            if (!data.isNullOrEmpty()) {
                _yourMutableLiveData.value = Resource.success(data)
            } else {
                val ex = handleException(e)
                _yourMutableLiveData.value = Resource.error(ex,null)
            }
        }
}

Не уверен, что это сработает, но вы можете попробовать добавить слушателя непосредственно в onResume() но будьте осторожны с созданием экземпляра ViewModel.

Небольшой совет, если вам не нужно значение, подобное Resource.loading(null), просто используйте sealed class с object

UPDATE Что касается вашего вопроса, который вы спрашиваете, почему он работает с функцией, а не с переменной, если вы вызовете этот метод в onResume, он будет выполнен снова. Вот в чем разница. Проверьте жизненный цикл Fragment или Activity, прежде чем переходить к материалу ViewModel.

...