在 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))

所以我不需要将数据传递给 FragmentActivity 来使用 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