简体   繁体   English

在 ViewModel 中观察 LiveData 最佳实践

[英]Observing LiveData in ViewModel best practices

I'm seeking the best way to observe data in ViewModel .我正在寻找在ViewModel中观察数据的最佳方式。

I'm using MVVM + DataBinding.我正在使用 MVVM + 数据绑定。

Repository:存储库:

private val data = MutableLiveData<String>()

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

It requests data from server and returns a live data.它从服务器请求数据并返回实时数据。 ViewModel must observe the data changes. 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 uses a MediatorLiveData to observe changes of the LiveData that comes from repository. ViewModel 使用MediatorLiveData来观察来自存储库的LiveData的变化。 I've added the data as a source to observe changes and remove it after it triggers to prevent firing events multiple times when I get data multiple times.我已将数据添加为源以观察更改并在触发后将其删除,以防止在我多次获取数据时多次触发事件。 And there must be a fake observer to MediatorLiveData so the onChange method of the MediatorLiveData triggers.并且MediatorLiveData必须有一个虚假的观察者,因此MediatorLiveData的 onChange 方法会触发。

Let's say that I just need the data to hide/show a view (or even fill data to my recyclerview's adaper).假设我只需要数据来隐藏/显示视图(甚至将数据填充到我的 recyclerview 适配器)。 Then I just call the below code and use an Observable and DataBinding like this:然后我只需调用下面的代码并使用这样的 Observable 和 DataBinding:

val adapter: ObservableField<DataAdapter> = ObservableField()
val recyclerviewVisibility: ObservableField<Int> = ObservableField(View.GONE)
...
...
recyclerviewVisibility.set(View.VISIBLE)
adapter.set(DataAdapter(dataList))

So I don't need to pass the data to Fragment or Activity to use the viewLifecycleOwner .所以我不需要将数据传递给FragmentActivity来使用viewLifecycleOwner I also cannot use observeForever in ViewModel because it may fire onChange method multiple times in some situations.我也不能在ViewModel中使用observeForever ,因为在某些情况下它可能会多次触发 onChange 方法。

Is there any better approach to get and observe data in ViewModel ?有没有更好的方法来获取和观察ViewModel中的数据?


Solution:解决方案:

I've found out that the best way to reach my goal is to get the data from repository without using LiveData :我发现达到我的目标的最好方法是在不使用LiveData的情况下从存储库中获取数据:

Repository存储库

suspend fun getData() : String{
    return  apiRequest { api.getData() }
}

ViewModel视图模型

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

fun gotData(data: String) {
    //use data
}

It's much simpler now.现在简单多了。


Bonus:奖金:

extension:扩大:

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

usage:用法:

request({request.getData()}) {
    //use data
}

If I figured the problem right, I think you could use the map transformation on the LiveData .如果我认为问题正确,我认为您可以在LiveData上使用map转换。

Maybe it's better to change the repository code like the following:也许最好更改存储库代码,如下所示:

private val reload = MutableLiveData<Unit>()

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

fun reload() {
    reload.postValue(Unit) 
}

Then, in your ViewModel using map transtormation, you can intercept the emmited values:然后,在使用 map 转换的ViewModel中,您可以截取发射值:

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

fun reload() {
    repository.reload() 
}

Using this structure, you will be able to call ViewModel's reload() everytime you need to, leading to fetch fresh data from api and emitting it on the ViewModel's data .使用此结构,您将能够在每次需要时调用 ViewModel 的reload() ,从而从 api 获取新数据并将其发送到 ViewModel 的data上。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM