LiveData 观察者触发了两次,即使有 viewLifecycleOwner

LiveData observer fired twice, even with viewLifecycleOwner

我正在与一个发射两次的 LiveData 观察器作斗争。在我的片段中,我正在观察如下 LiveData,使用 viewLifeCycleOwner 作为 LifeCycleOwner

private lateinit var retailViewModel: RetailsViewModel

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        retailViewModel =  ViewModelProviders.of(this).get(RetailsViewModel::class.java)
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {

  retailViewModel.retailLiveData.observe(viewLifecycleOwner, Observer {
    // updating UI here, but firing twice!
  }

  retailViewModel.getRetailById(retail.id)
} 

这是我的视图模型:

class RetailsViewModel(override val service: MyFoodyApiService = MyFoodyApiService.service) :
    BaseViewModel(service) {

    var retailLiveData: MutableLiveData<Retail> = MutableLiveData()

    fun getRetailById(id: Int) {
        scope.launch {
            try {
                val response =
                    service.getRetailById(authString, id).await()
                when (response.isSuccessful) {
                    true -> {
                        response.body()?.let { payload ->
                            retailLiveData.postValue(payload.data)
                        } ?: run {
                            errorLiveData.postValue("An error occurred: ${response.message()}")
                        }
                    }
                    false -> errorLiveData.postValue("An error occurred: ${response.message()}")
                }
            } catch (e: Exception) {
                noConnectionLiveData.postValue(true)
            }
        }
    }

}

当我第一次 运行 片段时,一切正常,但是当我转到它的 DetailFragment 并返回时,retailLiveData 观察者回调被触发两次。根据 this article 的说法,这是一个已知的问题,通过引入 viewLifeCycleOwner 应该有助于在片段的视图被破坏后移除活跃的观察者,但它似乎对我的情况没有帮助。

发生这种情况是因为当您打开另一个片段时视图模型保留值,但片段的视图被破坏。当您返回片段时,将重新创建视图并订阅 retailLiveData,它仍然保留先前的值并在片段移动到开始状态时立即通知您的观察者。但是你在 onViewCreated 中调用 retailViewModel.getRetailById(retail.id),所以过了一会儿,值会更新,观察者会再次收到通知。

一种可能的解决方案是从视图模型的 init 方法调用 getRetailById(),结果将在视图模型的生命周期内缓存。