如何在 ViewModel 中观察存储库 class 中的 return 值?

How to observe the return value from a Repository class in a ViewModel?

我有一个使用 MVVM 架构的 android 应用程序。在单击按钮时,我启动了一个调用 ViewModel 方法来发出网络请求的协程。在我的 ViewModel 中,我有一个 LiveData 可观察到该请求的 return,但我没有看到它更新。似乎我的存储库方法没有被调用,我不确定为什么。

UI 点击监听器

searchButton.setOnClickListener{
    CoroutineScope(IO).launch{
        viewModel.getUser(username.toString())
    }
}

ViewModel - Observable 和调用的方法

private var _user: MutableLiveData<User?> = MutableLiveData<User?>()
val user: LiveData <User?>
    get() = _user
...

suspend fun getUser(userId:String) {
   _user = liveData{
       emit(repository.getUser(userId))
   } as MutableLiveData<User?>
}
...

当我调试通过时,执行进入了 ViewModel 的 getUser 方法,但没有进入 liveData 范围来更新我的 _user MutableLiveData observable,我不确定我做错了什么。

不需要使用 liveData 协程生成器,因为 getUser 是一个挂起的函数,您已经在协程中调用它了。只是 post 结果只是 _user.

suspend fun getUser(userId: String) {
    _user.postValue(repository.getUser(userId))
}

您对代码所做的操作导致将 LiveData 的新实例分配给 _user,而片段中的观察者正在观察由 [= 实例化的前一个 LiveData 19=]。因此,更新丢失了。


更好的解决方案是在 ViewModel class 中处理协同程序的创建,以跟踪它们并防止执行泄漏。

fun getUser(userId: String) {
    viewModelScope.launch(IO) {
        _user.postValue(repository.getUser(userId))
    }
}

片段中:

searchButton.setOnClickListener{
    viewModel.getUser(username.toString())
}

它不起作用,因为您的“MVVM 结构”没有遵循 MVVM 建议,也没有遵循为协程提供的结构化并发指南。

    searchButton.setOnClickListener{
        CoroutineScope(IO).launch{ // <-- should be using a controlled scope
            viewModel.getUser(username.toString()) // <-- state belongs in the viewModel
        }
    }

相反,它应该看起来像这样

searchButton.setOnClickListener {
    viewModel.onSearchButtonClicked()
}

username.doAfterTextChanged {
    viewModel.updateUsername(it)
}

class MyViewModel(
    private val application: Application,
    private val savedStateHandle: SavedStateHandle
): AndroidViewModel(application) {
    private val repository = (application as CustomApplication).repository

    private val username = savedStateHandle.getLiveData("username", "")

    fun updateUsername(username: String) {
        username.value = username
    }
    
    val user: LiveData<User?> = username.switchMap { userId -> 
        liveData(viewModelScope + Dispatchers.IO) {
            emit(repository.getUser(userId))
        }
    }
}

现在您可以 user.observe(viewLifecycleOwner) { user -> ... } 并且应该可以了。如果您真的只需要在单击按钮时获取数据,您可能希望将 liveData { 替换为常规 suspend fun 调用,从 viewModelScope.launch { 调用,并将值保存到 LiveData .