Как очистить сохраненную ценность LiveData?

Согласно Документация LiveData:

Класс LiveData предоставляет следующие преимущества:

...

Всегда обновляемые данные: Если жизненный цикл снова запускается (например, операция возвращается к запущенному состоянию из заднего стека), он получает последние данные о местоположении (если он еще не был).

Но иногда мне не нужна эта функция.

Например, я вижу LiveData в ViewModel и Observer в действии:

//LiveData
val showDialogLiveData = MutableLiveData<String>()

//Activity
viewModel.showMessageLiveData.observe(this, android.arch.lifecycle.Observer { message ->
        AlertDialog.Builder(this)
                .setMessage(message)
                .setPositiveButton("OK") { _, _ -> }
                .show()
    })

Теперь после каждого поворота появится старое диалоговое окно.

Есть ли способ очистить сохраненное значение после его обработки или это неправильное использование LiveData?

Ответ 1

Обновить

Есть несколько способов решить эту проблему. Они кратко изложены в статье LiveData с SnackBar, Navigation и другими событиями (случай SingleLiveEvent). Это написано сотрудником Googler, который работает с командой компонентов архитектуры.

TL; DR Более надежный подход заключается в использовании класса-обертки Event, пример которого вы можете увидеть внизу статьи.

Этот шаблон попал в многочисленные образцы Android, например:

Почему оболочка Event предпочтительнее SingleLiveEvent?

Одна проблема с SingleLiveEvent состоит в том, что если существует несколько наблюдателей для SingleLiveEvent, только один из них будет уведомлен об изменении этих данных - это может привести к незначительным ошибкам и их трудно обойти.

Используя класс-оболочку Event, вы можете либо явно "обрабатывать" контент (контент может быть "обработан" только один раз), либо просматривать контент, который всегда возвращает то, что было последним "контентом". В примере диалога это означает, что вы всегда можете увидеть, что было последним сообщением с peek, но убедитесь, что для каждого нового сообщения диалог запускается только один раз, используя getContentIfNotHandled.

Старый ответ

Ответ Алексея в комментариях, я думаю, что именно то, что вы ищете. Там пример кода для класса под названием SingleLiveEvent. Цель этого класса описывается как:

Наблюдаемая жизненным циклом наблюдаемая, которая отправляет только новые обновления после подписки, используемая для таких событий, как навигация и сообщения Snackbar.

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

Ответ 2

В моем случае SingleLiveEvent не помогает. Я использую этот код:

private MutableLiveData<Boolean> someLiveData;
private final Observer<Boolean> someObserver = new Observer<Boolean>() {
    @Override
    public void onChanged(@Nullable Boolean aBoolean) {
        if (aBoolean != null) {
            // doing work
            ...

            // reset LiveData value  
            someLiveData.postValue(null);
        }
    }
};

Ответ 3

Я не уверен, будет ли это работать в вашем случае, но в моем случае (увеличение/уменьшение количества элементов в комнате путем нажатия на представления) удаление Observer и проверка наличия активных наблюдателей позволяют мне выполнить эту работу:

LiveData<MenuItem> menuitem = mViewModel.getMenuItemById(menuid);
menuitem.observe(this, (MenuItem menuItemRoom) ->{
                menuitem.removeObservers(this);
                if(menuitem.hasObservers())return;

                // Do your single job here

                });
});  

ОБНОВЛЕНИЕ 20/03/2019:

Теперь я предпочитаю это: класс EventWraper из Google Samples внутри MutableLiveData

/**
 * Used as a wrapper for data that is exposed via a LiveData that represents an event.
 */
public class Event<T> {

    private T mContent;

    private boolean hasBeenHandled = false;


    public Event( T content) {
        if (content == null) {
            throw new IllegalArgumentException("null values in Event are not allowed.");
        }
        mContent = content;
    }

    @Nullable
    public T getContentIfNotHandled() {
        if (hasBeenHandled) {
            return null;
        } else {
            hasBeenHandled = true;
            return mContent;
        }
    }

    public boolean hasBeenHandled() {
        return hasBeenHandled;
    }
}

В ViewModel:

 /** expose Save LiveData Event */
 public void newSaveEvent() {
    saveEvent.setValue(new Event<>(true));
 }

 private final MutableLiveData<Event<Boolean>> saveEvent = new MutableLiveData<>();

 LiveData<Event<Boolean>> onSaveEvent() {
    return saveEvent;
 }

В Деятельности/Фрагмент

mViewModel
    .onSaveEvent()
    .observe(
        getViewLifecycleOwner(),
        booleanEvent -> {
          if (booleanEvent != null)
            final Boolean shouldSave = booleanEvent.getContentIfNotHandled();
            if (shouldSave != null && shouldSave) saveData();
          }
        });

Ответ 4

Вы должны использовать SingleLiveEvent для этого случая

class SingleLiveEvent<T> : MutableLiveData<T>() {

    private val pending = AtomicBoolean(false)

    @MainThread
    override fun observe(owner: LifecycleOwner, observer: Observer<T>) {

        if (hasActiveObservers()) {
            Log.w(TAG, "Multiple observers registered but only one will be notified of changes.")
        }

        // Observe the internal MutableLiveData
        super.observe(owner, Observer<T> { t ->
            if (pending.compareAndSet(true, false)) {
                observer.onChanged(t)
            }
        })
    }

    @MainThread
    override fun setValue(t: T?) {
        pending.set(true)
        super.setValue(t)
    }

    /**
     * Used for cases where T is Void, to make calls cleaner.
     */
    @MainThread
    fun call() {
        value = null
    }

    companion object {
        private const val TAG = "SingleLiveEvent"
    }
}

И внутри вас viewmodel класс создать объект, как:

 val snackbarMessage = SingleLiveEvent<Int>()

Ответ 5

Если вам нужно простое решение, попробуйте это:

class SingleLiveData<T> : MutableLiveData<T?>() {

    override fun observe(owner: LifecycleOwner, observer: Observer<in T?>) {
        super.observe(owner, Observer { t ->
            if (t != null) {
                observer.onChanged(t)
                postValue(null)
            }
        })
    }
}

Используйте его как обычный MutableLiveData