在 ViewModel 中观察 LiveData 最佳实践
Observing LiveData in ViewModel best practices
我正在寻找观察 ViewModel
中数据的最佳方法。
我正在使用 MVVM + DataBinding。
存储库:
private val data = MutableLiveData<String>()
suspend fun getData(): LiveData<String> {
return withContext(IO) {
val response = apiRequest { api.getData() }
data.postValue(response)
data
}
}
它从服务器请求数据和 returns 实时数据。 ViewModel 必须观察数据变化。
ViewModel:
suspend fun getData() {
val data = repository.getData()
MediatorLiveData<String>().apply {
addSource(data) {
gotData(it)
removeSource(data)
}
observeForever { }
}
}
private fun gotData(data: String) {
//use data
}
ViewModel 使用 MediatorLiveData
来观察来自存储库的 LiveData
的变化。我已将数据添加为观察更改的来源,并在它触发后将其删除,以防止在我多次获取数据时多次触发事件。 MediatorLiveData
必须有一个假的观察者,所以 MediatorLiveData
的 onChange 方法会触发。
假设我只需要数据到 hide/show 一个视图(或者甚至将数据填充到我的 recyclerview 的适配器)。然后我只需调用下面的代码并使用这样的 Observable 和 DataBinding:
val adapter: ObservableField<DataAdapter> = ObservableField()
val recyclerviewVisibility: ObservableField<Int> = ObservableField(View.GONE)
...
...
recyclerviewVisibility.set(View.VISIBLE)
adapter.set(DataAdapter(dataList))
所以我不需要将数据传递给 Fragment
或 Activity
来使用 viewLifecycleOwner
。
我也不能在 ViewModel
中使用 observeForever
,因为在某些情况下它可能会多次触发 onChange 方法。
是否有更好的方法来获取和观察 ViewModel
中的数据?
解法:
我发现实现我的目标的最佳方法是在不使用 LiveData
:
的情况下从存储库中获取数据
存储库
suspend fun getData() : String{
return apiRequest { api.getData() }
}
ViewModel
suspend fun getData(){
val data = repository.getData()
gotData(data)
}
fun gotData(data: String) {
//use data
}
现在简单多了。
奖金:
分机:
fun <T : Any> ViewModel.request(request: suspend () -> (T), result: (T) -> (Unit) = {}) {
viewModelScope.launch {
result(request())
}
}
用法:
request({request.getData()}) {
//use data
}
如果我猜对了问题,我想你可以在 LiveData
.
上使用 map
转换
也许像下面这样更改存储库代码会更好:
private val reload = MutableLiveData<Unit>()
val data: LiveData<String> =
reload.switchMap {
liveData(IO) {
emit(apiRequest { api.getData() })
}
}
fun reload() {
reload.postValue(Unit)
}
然后,在你的 ViewModel
使用地图转换时,你可以截取 emmited 值:
val data: LiveData<String> =
repository.data.map {
gotData(it)
it
}
fun reload() {
repository.reload()
}
使用此结构,您将能够在每次需要时调用 ViewModel 的 reload()
,从而从 api 获取新数据并将其发送到 ViewModel 的 data
。
我正在寻找观察 ViewModel
中数据的最佳方法。
我正在使用 MVVM + DataBinding。
存储库:
private val data = MutableLiveData<String>()
suspend fun getData(): LiveData<String> {
return withContext(IO) {
val response = apiRequest { api.getData() }
data.postValue(response)
data
}
}
它从服务器请求数据和 returns 实时数据。 ViewModel 必须观察数据变化。
ViewModel:
suspend fun getData() {
val data = repository.getData()
MediatorLiveData<String>().apply {
addSource(data) {
gotData(it)
removeSource(data)
}
observeForever { }
}
}
private fun gotData(data: String) {
//use data
}
ViewModel 使用 MediatorLiveData
来观察来自存储库的 LiveData
的变化。我已将数据添加为观察更改的来源,并在它触发后将其删除,以防止在我多次获取数据时多次触发事件。 MediatorLiveData
必须有一个假的观察者,所以 MediatorLiveData
的 onChange 方法会触发。
假设我只需要数据到 hide/show 一个视图(或者甚至将数据填充到我的 recyclerview 的适配器)。然后我只需调用下面的代码并使用这样的 Observable 和 DataBinding:
val adapter: ObservableField<DataAdapter> = ObservableField()
val recyclerviewVisibility: ObservableField<Int> = ObservableField(View.GONE)
...
...
recyclerviewVisibility.set(View.VISIBLE)
adapter.set(DataAdapter(dataList))
所以我不需要将数据传递给 Fragment
或 Activity
来使用 viewLifecycleOwner
。
我也不能在 ViewModel
中使用 observeForever
,因为在某些情况下它可能会多次触发 onChange 方法。
是否有更好的方法来获取和观察 ViewModel
中的数据?
解法:
我发现实现我的目标的最佳方法是在不使用 LiveData
:
存储库
suspend fun getData() : String{
return apiRequest { api.getData() }
}
ViewModel
suspend fun getData(){
val data = repository.getData()
gotData(data)
}
fun gotData(data: String) {
//use data
}
现在简单多了。
奖金:
分机:
fun <T : Any> ViewModel.request(request: suspend () -> (T), result: (T) -> (Unit) = {}) {
viewModelScope.launch {
result(request())
}
}
用法:
request({request.getData()}) {
//use data
}
如果我猜对了问题,我想你可以在 LiveData
.
map
转换
也许像下面这样更改存储库代码会更好:
private val reload = MutableLiveData<Unit>()
val data: LiveData<String> =
reload.switchMap {
liveData(IO) {
emit(apiRequest { api.getData() })
}
}
fun reload() {
reload.postValue(Unit)
}
然后,在你的 ViewModel
使用地图转换时,你可以截取 emmited 值:
val data: LiveData<String> =
repository.data.map {
gotData(it)
it
}
fun reload() {
repository.reload()
}
使用此结构,您将能够在每次需要时调用 ViewModel 的 reload()
,从而从 api 获取新数据并将其发送到 ViewModel 的 data
。