Android : Live Data пропускает значения

Я использую Live Data для публикации состояний из View Model во Fragments, это может привести к частой публикации состояний. Но изменяемые живые данные пропускают начальные значения и берут самое последнее доступное значение.

Существует статья, в которой говорится об этой характеристике, но есть ли способ обработки этого случая, например Flowable в RxJava или установить Стратегию обратного давления, или мне нужно будет вернуться к использованию RxJava и обрабатывать публикации на основе жизненного цикла?

Ниже приведен пример кода, демонстрирующий такое поведение. Публикуются значения от 1 до 10, но принимаются только два значения, 0 и 10. Можем ли мы изменить это поведение в Live Data или мне следует использовать для этой цели RxJava?

Фрагмент (подписчик):

class ParentFragment : Fragment() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        viewModel = ViewModelProviders.of(
            this, ParentViewModelFactory(this, null)
        ).get(ParentViewModel::class.java)

        viewModel.fastLiveData.observe(this, Observer {
            Timber.i(it.toString())
        })

        viewModel.startPublishing()
    }
}

Модель представления (издатель):

class ParentViewModel(private val savedState : SavedStateHandle)
    : ViewModel<ParentState>() {

    val fastLiveData : MutableLiveData<Int> = MutableLiveData(0)

    fun startPublishing() {
        for(x in 1..10) {
            Timber.i(x.toString())
            fastLiveData.postValue(x)
        }
    }
}

Вывод:

(ParentViewModel.kt:30)#startPublishing: 1
(ParentViewModel.kt:30)#startPublishing: 2
(ParentViewModel.kt:30)#startPublishing: 3
(ParentViewModel.kt:30)#startPublishing: 4
(ParentViewModel.kt:30)#startPublishing: 5
(ParentViewModel.kt:30)#startPublishing: 6
(ParentViewModel.kt:30)#startPublishing: 7
(ParentViewModel.kt:30)#startPublishing: 8
(ParentViewModel.kt:30)#startPublishing: 9
(ParentViewModel.kt:30)#startPublishing: 10
(ParentFragment.kt:57)#onChanged: 0
(ParentFragment.kt:57)#onChanged: 10

person ashwin mahajan    schedule 14.10.2019    source источник


Ответы (3)


Причина в том, что вы используете postValue()

  • postValue установит значение в фоновом потоке и установит самое последнее значение в случае большого выброса.
  • setValue установит ваше значение в основном потоке

ЕСЛИ вы измените на fastLiveData.setValue(x), вы получите желаемое поведение.

person Mustafa Ozhan    schedule 04.06.2020
comment
Это может ввести в заблуждение. postValue можно использовать в фоновом режиме, и значение будет установлено в основном потоке асинхронно. setValue немедленно установит значение в текущем потоке, но текущий поток должен быть основным потоком. - person khcpietro; 11.11.2020

Используйте 1_. LiveData предназначен для наблюдения на уровне представления основным потоком. Для LiveData наблюдателей нет смысла потреблять больше, чем частота обновления экрана или человеческий глаз может выдержать.

Использование RxJava и LiveData в одном проекте является законным выбором дизайна. В Интернете должно быть несколько статей и примеров кода, вот один, который я только что нашел: https://proandroiddev.com/mvvm-architecture-using-livedata-rxjava-and-new-dagger-android-injection-639837b1eb6c

person Sanlok Lee    schedule 14.10.2019
comment
Не могли бы вы предоставить ссылки на это: для наблюдателей LiveData нет смысла потреблять больше, чем частота обновления экрана или человеческий глаз может выдержать? - person ashwin mahajan; 15.10.2019
comment
Заявление относится к Android в целом, а не только к LiveData. Если вы не заметили, LiveData наблюдатели вызываются только в потоке пользовательского интерфейса — для обновления пользовательского интерфейса. Таким образом, любое чрезмерное обновление пользовательского интерфейса будет либо потрачено впустую, либо заблокирует поток пользовательского интерфейса, и, следовательно, наблюдателям живых данных не имеет смысла потреблять больше, чем может обработать поток пользовательского интерфейса. LiveData обеспечивает возможную согласованность между пользовательским интерфейсом и данными, но не говорит, что уведомления будут буферизоваться. - person Sanlok Lee; 15.10.2019
comment
То же самое относится и к RxJava. Даже если вы используете RxJava, высокочастотная работа должна выполняться в ViewModel с рабочим потоком и избегать слишком частой диспетчеризации для слоя просмотра. Тем не менее, если вы готовы пожертвовать производительностью, вы можете просто использовать LiveData#setValue вместо LiveData#postValue, но я предполагаю, что вы этого не хотели. - person Sanlok Lee; 15.10.2019

Корутины приносят решение этой проблемы:

Пример:

events.value = "Event A"
events.value = "Event B"
events.value = "Event C"

Это действительно только заполняет событие C для наблюдателя.

Однако, если вы оберните свой код сопрограммой, он будет работать:

viewModelScope.launch(Dispatchers.Main) {
    events.value = "Event A"
    events.value = "Event B"
    events.value = "Event C"
}
person Jacek Kwiecień    schedule 12.02.2021
comment
Это сработало для меня. Активность/фрагмент не получала все события, отправленные при использовании postValue(X). Но с помощью этого метода все события поступают в представление. - person R. Campos; 24.05.2021