【问题标题】:How to observe the return value from a Repository class in a ViewModel?如何观察 ViewModel 中 Repository 类的返回值?
【发布时间】:2021-03-30 22:39:23
【问题描述】:

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

UI 点击监听器

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

ViewModel - Observables 和调用的方法

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,我不确定我在做什么错了。

【问题讨论】:

    标签: android kotlin android-livedata kotlin-coroutines android-mvvm


    【解决方案1】:

    没有必要使用liveData 协程构建器,因为getUser 是一个挂起的函数,并且您已经在协程中调用它。只需在_user 上发布结果即可。

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

    您对代码所做的操作导致将LiveData 的新实例分配给_user,而片段中的观察者正在观察由private var _user: MutableLiveData&lt;User?&gt; = MutableLiveData&lt;User?&gt;() 实例化的先前LiveData。所以,更新会丢失。


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

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

    在片段中:

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

    【讨论】:

      【解决方案2】:

      它不起作用,因为您的“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 -&gt; ... },它应该可以工作了。如果您确实需要仅在单击按钮时获取,您可能希望将 liveData { 替换为常规的 suspend fun 调用,从 viewModelScope.launch { 调用,并将值保存到 LiveData。

      【讨论】:

      • 非常感谢您的反馈。我不完全理解状态应该由 MVVM 中的 ViewModel 封装,所以这会有所帮助。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多