LiveData не работает при сохранении в ViewModel

Я реализую LiveData для извлечения пользовательских данных в свой ProfileFragment. Я храню переменную LiveData в ViewModel, чтобы ViewModel не возвращала новые LiveData каждый раз, когда она вызывается.

Однако LiveData не обновляется во второй раз, он может отображать пользовательские данные в начале, но когда пользователь редактирует свои данные/информацию, текст профиля не обновляется, а показывает пустой текст (ничего не показывает).

Я предполагаю, что это связано с тем, что Observer никогда не получал правильное значение, но как это могло произойти?

Фрагмент профиля

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
    }

person Risal Fajar Amiyardi    schedule 09.05.2020    source источник
comment
Я думаю, это потому, что вы использовали addListenerForSingleValueEvent. Вместо этого попробуйте addValueEventListener.   -  person Nicolas    schedule 09.05.2020
comment
@Nicolas обратите внимание, что: viewModel.getUserData вызывается onViewCreated и каждый раз, когда пользователь завершает редактирование своих данных/информации о профиле. Вот почему я использую событие с одним значением.   -  person Risal Fajar Amiyardi    schedule 09.05.2020


Ответы (3)


Мустафа Халед: Я не думаю, что это проблема. viewmodelfactory должен возвращать один и тот же экземпляр модели представления, при выполнении это не имеет значения. Собственно, поэтому мы и пользуемся услугами этого провайдера.

Я думаю, вы можете найти решение реальной проблемы здесь:

Разница между addValueEventListener() и addListenerForSingleValueEvent() в firebase

person user2418366    schedule 09.05.2020
comment
Обратите внимание, что: viewModel.getUserData вызывается для onViewCreated И каждый раз, когда пользователь завершает редактирование своих данных/профиля. Вот почему я использую SingleValueEvent. - person Risal Fajar Amiyardi; 09.05.2020
comment
Посмотрите, в вашем коде есть проблема: вызов viewModel.getUserData() изменяет ссылку на userDataLiveData. Итак, сначала вы начинаете наблюдать живые данные, созданные при первом вызове. Но после следующего вызова вы создаете другие живые данные. И вы будете обновлять значения этих новых данных в реальном времени, но вы продолжаете наблюдать за новыми. - person user2418366; 09.05.2020

Посмотрите, в вашем коде есть проблема: вызов 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...

Это быстрое решение с возможным рефакторингом, но оно должно решить проблему.

person user2418366    schedule 09.05.2020
comment
Разве вызов getUserData().observe во фрагменте не будет каждый раз создавать нового наблюдателя? Моя идея состоит в том, чтобы сохранить переменную LiveData в ViewModel, чтобы я просто наблюдал ее один раз в onViewCreated - person Risal Fajar Amiyardi; 10.05.2020
comment
Он начинает наблюдать каждый раз, когда вызывается созданное представление. Но воссоздается ли фрагмент перед вызовом getUserData, когда пользователь заканчивает редактировать свои данные/информацию о профиле? Не могли бы вы вставить код, выполняющий этот вызов? Я подозреваю, что существует тот же фрагмент, поэтому наблюдайте за старыми данными, но новые живые данные после того, как getuserdata обновляются с измененным значением. Вы можете легко задать точки останова отладчику и проверить эту последовательность. - person user2418366; 10.05.2020
comment
Я что-то пробовал, я думаю, проблема в ViewModel, может быть, незаконно переназначать ViewModel LiveData? Я имею в виду следующее: userDataLiveData = DatabaseRepository.getUserData(uid) я пришел к такому выводу, потому что после того, как я попытался использовать MediatorLiveData, он выполняет свою работу. - person Risal Fajar Amiyardi; 10.05.2020
comment
Что я сделал, так это изменил ViewModel на это: private val _userDataLiveData = MediatorLiveData<Resource<User>>() var userDataLiveData: LiveData<Resource<User>> = _userDataLiveData fun getUserData() { val data = DatabaseRepository.getUserData(uid) _userDataLiveData.addSource(data){ if(it !is Resource.Loading) _userDataLiveData.removeSource(data) _userDataLiveData.value = it } } - person Risal Fajar Amiyardi; 10.05.2020
comment
Это было моей точкой зрения, не используйте снова и снова другие livedata. Да, будет намного лучше, если livedata во фрагменте будет val. В текущем решении вы используете один livedata, и теперь все должно быть в порядке. - person user2418366; 10.05.2020

Я предполагаю, что проблема, с которой вы столкнулись, заключается в инициализации нового экземпляра ViewModel.

Вместо добавления:

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

в вашем onViewCreated() добавьте его в onCreate(), чтобы ваша ViewModel инициализировалась один раз.

Попробуйте, надеюсь, это поможет. Если не любезный комментарий.

Удачного кодирования ????

person MustafaKhaled    schedule 09.05.2020