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の誤った使用方法はありますか?
この問題を解決するには、実際にはいくつかの方法があります。これらは記事 SnackBar、ナビゲーション、その他のイベント(SingleLiveEventの場合)を使用したLiveData にまとめられています。これは、アーキテクチャコンポーネントチームと協力するGoogle社員によって書かれています。
TL; DRより堅牢なアプローチは、 イベントラッパークラス を使用することです。 記事 。
このパターンにより、多数のAndroidサンプルに次のようになりました。
なぜSingleLiveEventよりもイベントラッパーが優先されるのですか?
SingleLiveEventの問題の1つは、SingleLiveEventに複数のオブザーバーがある場合、そのデータが変更されたときにそのうちの1人だけに通知されることです。これにより、微妙なバグが発生し、回避が困難になります。
イベントラッパークラスを使用すると、すべてのオブザーバーに通常どおり通知されます。その後、コンテンツを明示的に「処理」するか(コンテンツは1回のみ「処理」)、コンテンツを覗くかを選択できます。これにより、常に最新の「コンテンツ」が返されます。ダイアログの例では、これは常にpeek
で最後のメッセージが何であったかを見ることができることを意味しますが、getContentIfNotHandled
を使用して、新しいメッセージごとにダイアログが1回だけトリガーされることを確認します。
コメントでのアレックスの反応は、まさにあなたが探しているものだと思います。 SingleLiveEvent というクラスのサンプルコードがあります。このクラスの目的は次のとおりです。
サブスクリプション後に新しい更新のみを送信し、ナビゲーションやスナックバーメッセージなどのイベントに使用される、ライフサイクルを意識したオブザーバブル。
これにより、イベントに関する一般的な問題を回避できます。構成の変更(ローテーションなど)で、オブザーバーがアクティブな場合に更新を発行できます。このLiveDataは、setValue()またはcall()の明示的な呼び出しがある場合にのみ、observableを呼び出します。
私の場合、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);
}
}
};
私はそれがあなたのケースでうまくいくかどうかはわかりませんが、私の場合(ビューをクリックすることで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();
}
});
この場合、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>()
私が見つけた最良の解決策は、 ライブイベントライブラリ です。これは、複数のオブザーバーがいる場合に完全に機能します。
class LiveEventViewModel : ViewModel() {
private val clickedState = LiveEvent<String>()
val state: LiveData<String> = clickedState
fun clicked() {
clickedState.value = ...
}
}
簡単な解決策が必要な場合は、これを試してください:
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のように使用します