【问题标题】:LiveData does not work when stored in ViewModelLiveData 在 ViewModel 中存储时不起作用
【发布时间】:2020-05-09 14:41:03
【问题描述】:

我正在实施 LiveData 以将用户数据提取到我的 ProfileFragment。我将 LiveData 变量存储在 ViewModel 中,这样 ViewModel 就不会在每次调用时都返回新的 LiveData。

但是 LiveData 没有第二次更新,它可以在开始时显示用户数据,但是当用户编辑其数据/信息时,配置文件文本不会更新而是显示空白文本(不显示任何内容)。

我认为这是由于 Observer 从未收到正确的值,但这怎么会发生呢?

ProfileFragment

viewModel.getUserData 被调用 onViewCreated 并且每次用户完成编辑他的数据/个人资料信息时

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

        viewModel = ViewModelProvider.AndroidViewModelFactory(activity!!.application).create(AboutViewModel::class.java)

        viewModel.getUserData()
        viewModel.userDataLiveData.observe(viewLifecycleOwner, Observer {
            when (it) {
                is Resource.Success -> {
                    text_account_name.text = it.data.name
                    text_account_email.text = it.data.email
                    text_account_phone_number.text = it.data.phoneNumber
                }
                is Resource.Failure -> {
                    when (it.throwable) {
                        is UserDataEmptyException -> startFillDataActivity()
                    }
                }
            }
        })
    }

视图模型

var userDataLiveData: LiveData<Resource<User>> = MutableLiveData()
fun getUserData() {
        userDataLiveData = DatabaseRepository.getUserData(uid)
    }

数据库存储库

fun getUserData(uid: String): LiveData<Resource<User>>{
        val result = MutableLiveData<Resource<User>>().apply {
            value = Resource.Loading()
        }

        userRef.child(uid).addListenerForSingleValueEvent(object : ValueEventListener{
            override fun onDataChange(p0: DataSnapshot) {
                val name = p0.child(FIELD_NAME).value.toString()
                val email = p0.child(FIELD_EMAIL).value.toString()
                val phoneNumber = p0.child(FIELD_PHONE_NUMBER).value.toString()
                val user = User(name, email, phoneNumber)

                if(checkIfUserDataComplete(user)) result.value = Resource.Success(user)
                else result.value = Resource.Failure(UserDataEmptyException("User have no data"))
            }

            override fun onCancelled(p0: DatabaseError) {
                result.value = Resource.Failure(p0.toException())
            }
        })

        return result
    }

【问题讨论】:

  • 我想是因为你使用了addListenerForSingleValueEvent。请改用addValueEventListener
  • @Nicolas 请注意: viewModel.getUserData 在 onViewCreated 上调用,并且每次用户完成编辑他的数据/配置文件信息时。这就是我使用单值事件的原因。

标签: android android-livedata android-mvvm


【解决方案1】:

MustafaKhaled 我认为这不是问题。 viewmodelfactory 应该返回相同的 viewmodel 实例,执行时无关紧要。实际上这就是我们使用此提供程序的原因。

我认为您可以在这里找到解决实际问题的方法:

Difference between addValueEventListener() and addListenerForSingleValueEvent() of firebase

【讨论】:

  • 请注意:“viewModel.getUserData 在 onViewCreated 并且每次用户完成编辑他的数据/配置文件信息时被调用”。这就是我使用 SingleValueEvent 的原因。
  • 看,您的代码中存在问题,调用 viewModel.getUserData() 会更改 userDataLiveData 引用。因此,首先您开始观察第一次调用时创建的实时数据。但是在下一次调用之后,您会创建另一个实时数据。而且您将更新新实时数据的值,但您会继续观察新数据。
【解决方案2】:

看,您的代码中存在问题,调用 viewModel.getUserData() 会更改 userDataLiveData 引用。因此,首先您开始观察第一次调用时创建的实时数据。但是在下一次调用之后,您会创建另一个实时数据。而且您将更新新实时数据的值,但您会继续观察旧数据。

您应该继续使用相同的实时数据,例如 存储库

private val result: LiveData<Resource<User>> =  MutableLiveData<Resource<User>>()
fun getUserData(uid: String): LiveData<Resource<User>>{
        result.value = Resource.Loading()

        userRef.child(uid).addListenerForSingleValueEvent(object : ValueEventListener{
            override fun onDataChange(p0: DataSnapshot) {
                val name = p0.child(FIELD_NAME).value.toString()
                val email = p0.child(FIELD_EMAIL).value.toString()
                val phoneNumber = p0.child(FIELD_PHONE_NUMBER).value.toString()
                val user = User(name, email, phoneNumber)

                if(checkIfUserDataComplete(user)) result.value = Resource.Success(user)
                else result.value = Resource.Failure(UserDataEmptyException("User have no data"))
            }

            override fun onCancelled(p0: DatabaseError) {
                result.value = Resource.Failure(p0.toException())
            }

视图模型

getUserData() = DatabaseRepository.getUserData(uid)

片段

getUserData().observe...

这是一个快速的解决方案,可能需要进行重构,但应该可以解决问题。

【讨论】:

  • 不会在片段中调用getUserData().observe 每次都会创建一个新的观察者吗?我的想法是将 LiveData 变量存储在 ViewModel 中,这样我只需在 onViewCreated 中观察一次
  • 每次调用创建的视图时都会开始观察。但是当用户完成编辑他的数据/配置文件信息时,在调用 getUserData 之前是否重新创建了片段?您能否粘贴进行此调用的代码?我怀疑存在相同的片段,因此观察旧数据,但是 getuserdata 之后的新 livedata 更新了更改的值。您可以轻松地为调试器设置断点并验证此序列。
  • 我一直在尝试,我认为问题出在 ViewModel 上,也许重新分配 ViewModel LiveData 是非法的?我的意思是:userDataLiveData = DatabaseRepository.getUserData(uid) 我之所以有这种想法是因为在我尝试使用 MediatorLiveData 之后,它就完成了这项工作
  • 我所做的是将 ViewModel 更改为:private val _userDataLiveData = MediatorLiveData&lt;Resource&lt;User&gt;&gt;()var userDataLiveData: LiveData&lt;Resource&lt;User&gt;&gt; = _userDataLiveDatafun getUserData() { val data = DatabaseRepository.getUserData(uid) _userDataLiveData.addSource(data){ if(it !is Resource.Loading) _userDataLiveData.removeSource(data) _userDataLiveData.value = it } }
  • 这是我的观点,不要一次又一次地使用另一个 livedata。是的,如果片段中的 livedata 是 val 会更好。在当前的解决方案中,您使用的是一个 livedata,现在应该没问题了。
【解决方案3】:

我猜你面临的问题是,你初始化了一个新的 ViewModel 实例。

而不是添加:

        viewModel = ViewModelProvider.AndroidViewModelFactory(activity!!.application).create(AboutViewModel::class.java)

在您的 onViewCreated() 中,将其添加到 onCreate() 中,这样您的 ViewModel 就会被初始化一次。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-07-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-16
    • 1970-01-01
    • 2021-12-28
    • 1970-01-01
    相关资源
    最近更新 更多