У меня есть отдельный класс, в котором я обрабатываю выборку данных (в частности, Firebase), и я обычно возвращаю из него объекты LiveData и обновляю их асинхронно. Теперь я хочу, чтобы возвращенные данные хранились в ViewModel, но проблема в том, что для получения указанного значения мне нужно наблюдать за объектом LiveData, возвращаемым из моего класса выборки данных. Для метода наблюдения требуется объект LifecycleOwner в качестве первого параметра, но у меня, очевидно, нет этого внутри моей ViewModel, и я знаю, что я не должен хранить ссылку на Activity / Fragment внутри ViewModel. Что я должен делать?
91
Ответы:
В этом сообщении блога разработчика Google Хосе Альсерреки рекомендуется использовать преобразование в этом случае (см. Параграф «LiveData в репозиториях»), потому что ViewModel не должен содержать никаких ссылок, связанных с
View
(Activity, Context и т. Д.), Потому что это затрудняет тестировать.источник
В документации ViewModel
Другой способ - реализовать RxJava вместо LiveData для данных, тогда у них не будет преимуществ, связанных с жизненным циклом.
В примере Google todo-mvvm-live-kotlin используется обратный вызов без LiveData в ViewModel.
Я предполагаю, что если вы хотите полностью соответствовать идее того, чтобы быть продуктом жизненного цикла, нам нужно переместить код наблюдения в Activity / Fragment. В противном случае мы можем использовать обратный вызов или RxJava в ViewModel.
Другой компромисс - реализовать MediatorLiveData (или Transformations) и наблюдать (разместить здесь свою логику) в ViewModel. Обратите внимание, что наблюдатель MediatorLiveData не сработает (как и преобразования), если он не будет обнаружен в Activity / Fragment. Что мы делаем, так это помещаем пустое наблюдение в Activity / Fragment, где реальная работа фактически выполняется в ViewModel.
// ViewModel fun start(id : Long) : LiveData<User>? { val liveData = MediatorLiveData<User>() liveData.addSource(dataSource.getById(id), Observer { if (it != null) { // put your logic here } }) } // Activity/Fragment viewModel.start(id)?.observe(this, Observer { // blank observe here })
PS: Я прочитал ViewModels и LiveData: Patterns + AntiPatterns, в которых говорилось, что Transformations. Я не думаю, что это сработает, если не соблюдаются LiveData (что, вероятно, требует, чтобы это было сделано в Activity / Fragment).
источник
mLiveData.asFlow()
) илиobserveForever
.Я думаю, вы можете использовать ObserverForever, который не требует интерфейса владельца жизненного цикла, и вы можете наблюдать результаты из модели просмотра.
источник
Cannot invoke observeForever on a background thread
onCleared
. Что касается фонового потока - наблюдайте из основного потока, вот и все.observeForever
вызывать из основного черезGlobalScope.launch(Dispatchers.Main) { myvm.observeForever() }
Используйте сопрограммы Kotlin с компонентами архитектуры.
Вы можете использовать
liveData
функцию построителя для вызоваsuspend
функции, обслуживающей результат какLiveData
объект.val user: LiveData<User> = liveData { val data = database.loadUser() // loadUser is a suspend function. emit(data) }
Вы также можете передать несколько значений из блока. Каждый
emit()
вызов приостанавливает выполнение блока до тех пор, покаLiveData
значение не будет установлено в основном потоке.val user: LiveData<Result> = liveData { emit(Result.loading()) try { emit(Result.success(fetchUser())) } catch(ioException: Exception) { emit(Result.error(ioException)) } }
В вашей конфигурации gradle используйте
androidx.lifecycle:lifecycle-livedata-ktx:2.2.0
или выше.Об этом тоже есть статья .
Обновление : также можно изменить
LiveData<YourData>
вDao
interface
. К функции нужно добавитьsuspend
ключевое слово:@Query("SELECT * FROM the_table") suspend fun getAll(): List<YourData>
и в нем
ViewModel
вам нужно получить его асинхронно вот так:viewModelScope.launch(Dispatchers.IO) { allData = dao.getAll() // It's also possible to sync other data here }
источник