在 ViewModel 中观察 LiveData 最佳实践

Ami*_*mir 6 android mvvm viewmodel kotlin android-livedata

我正在寻找观察数据的最佳方法ViewModel

我正在使用 MVVM + 数据绑定。

存储库:

private val data = MutableLiveData<String>()

suspend fun getData(): LiveData<String> {
        return withContext(IO) {
            val response = apiRequest { api.getData() }
            data.postValue(response)
            data
        }
    }
Run Code Online (Sandbox Code Playgroud)

它从服务器请求数据并返回实时数据。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
    }
Run Code Online (Sandbox Code Playgroud)

ViewModel 使用来观察来自存储库MediatorLiveData的更改。LiveData我已将数据添加为源以观察更改并在触发后将其删除,以防止在多次获取数据时多次触发事件。并且必须有一个假观察者来MediatorLiveData触发 onChange 方法MediatorLiveData

假设我只需要数据来隐藏/显示视图(或者甚至将数据填充到我的 recyclerview 的适配器)。然后我只需调用下面的代码并使用 Observable 和 DataBinding,如下所示:

val adapter: ObservableField<DataAdapter> = ObservableField()
val recyclerviewVisibility: ObservableField<Int> = ObservableField(View.GONE)
...
...
recyclerviewVisibility.set(View.VISIBLE)
adapter.set(DataAdapter(dataList))
Run Code Online (Sandbox Code Playgroud)

所以我不需要将数据传递给FragmentActivity使用viewLifecycleOwner. 我也不能使用observeForeverin ViewModel,因为在某些情况下它可能会多次触发 onChange 方法。

有没有更好的方法来获取和观察数据ViewModel


解决方案 :

我发现实现我的目标的最佳方法是从存储库获取数据而不使用LiveData

存储库

suspend fun getData() : String{
    return  apiRequest { api.getData() }
}
Run Code Online (Sandbox Code Playgroud)

视图模型

suspend fun getData(){
   val data = repository.getData()
    gotData(data)
}

fun gotData(data: String) {
    //use data
}
Run Code Online (Sandbox Code Playgroud)

现在简单多了。


奖金:

扩大:

fun <T : Any> ViewModel.request(request: suspend () -> (T), result: (T) -> (Unit) = {}) {
    viewModelScope.launch {
        result(request())
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

request({request.getData()}) {
    //use data
}
Run Code Online (Sandbox Code Playgroud)

ami*_*phy 4

如果我认为问题正确,我认为您可以mapLiveData.

也许最好更改存储库代码,如下所示:

private val reload = MutableLiveData<Unit>()

val data: LiveData<String> =
    reload.switchMap {
        liveData(IO) {
            emit(apiRequest { api.getData() })
        }
    }

fun reload() {
    reload.postValue(Unit) 
}
Run Code Online (Sandbox Code Playgroud)

然后,在ViewModel使用地图变换时,您可以拦截发射值:

val data: LiveData<String> = 
    repository.data.map {
        gotData(it)
        it
    }

fun reload() {
    repository.reload() 
}
Run Code Online (Sandbox Code Playgroud)

使用此结构,您将能够reload()在每次需要时调用 ViewModel,从而从 api 获取新数据并将其发送到 ViewModel 的data.