web-dev-qa-db-ja.com

LiveDataに保存された値をクリアする方法は?

LiveDataドキュメント によると:

LiveDataクラスには、次の利点があります。

...

常に最新のデータ:ライフサイクルが再び開始すると(アクティビティがバックスタックから開始状態に戻るなど)、最新の位置データを受信します(まだなかった場合)。

ただし、この機能が不要な場合があります。

たとえば、ViewModelには次のLiveDataがあり、Activityには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の誤った使用方法はありますか?

32
Kamer358

更新

この問題を解決するには、実際にはいくつかの方法があります。これらは記事 SnackBar、ナビゲーション、その他のイベント(SingleLiveEventの場合)を使用したLiveData にまとめられています。これは、アーキテクチャコンポーネントチームと協力するGoogle社員によって書かれています。

TL; DRより堅牢なアプローチは、 イベントラッパークラス を使用することです。 記事

このパターンにより、多数のAndroidサンプルに次のようになりました。

なぜSingleLiveEventよりもイベントラッパーが優先されるのですか?

SingleLiveEventの問題の1つは、SingleLiveEventに複数のオブザーバーがある場合、そのデータが変更されたときにそのうちの1人だけに通知されることです。これにより、微妙なバグが発生し、回避が困難になります。

イベントラッパークラスを使用すると、すべてのオブザーバーに通常どおり通知されます。その後、コンテンツを明示的に「処理」するか(コンテンツは1回のみ「処理」)、コンテンツを覗くかを選択できます。これにより、常に最新の「コンテンツ」が返されます。ダイアログの例では、これは常にpeekで最後のメッセージが何であったかを見ることができることを意味しますが、getContentIfNotHandledを使用して、新しいメッセージごとにダイアログが1回だけトリガーされることを確認します。

古い応答

コメントでのアレックスの反応は、まさにあなたが探しているものだと思います。 SingleLiveEvent というクラスのサンプルコードがあります。このクラスの目的は次のとおりです。

サブスクリプション後に新しい更新のみを送信し、ナビゲーションやスナックバーメッセージなどのイベントに使用される、ライ​​フサイクルを意識したオブザーバブル。

これにより、イベントに関する一般的な問題を回避できます。構成の変更(ローテーションなど)で、オブザーバーがアクティブな場合に更新を発行できます。このLiveDataは、setValue()またはcall()の明示的な呼び出しがある場合にのみ、observableを呼び出します。

36
Lyla

私の場合、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);
        }
    }
};
4
Final12345

私はそれがあなたのケースでうまくいくかどうかはわかりませんが、私の場合(ビューをクリックすることでRoom内のアイテムの量を増減)オブザーバーを削除し、アクティブなオブザーバーがあるかどうかを確認してください:

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

                // Do your single job here

                });
});  

PDATE 20/03/2019:

今私はこれを好む:MutableLiveData内のGoogleサンプルからのEventWraperクラス

/**
 * 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();
          }
        });
1
Jurij Pitulja

この場合、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>()
1
Ankit Bisht

私が見つけた最良の解決策は、 ライブイベントライブラリ です。これは、複数のオブザーバーがいる場合に完全に機能します。

class LiveEventViewModel : ViewModel() {
    private val clickedState = LiveEvent<String>()
    val state: LiveData<String> = clickedState

    fun clicked() {
        clickedState.value = ...
    }
}
0
Morteza Rastgoo

簡単な解決策が必要な場合は、これを試してください:

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のように使用します

0
Nynuzoud