MutableLiveDataのsetValue()とpostValue()の違い


104

の値を変更するには、2つの方法がありますMutableLiveData。しかし、違いは何ですsetValue()postValue()ではMutableLiveData

同じドキュメントは見つかりませんでした。

こちらがMutableLiveDataAndroidのクラスです。

package android.arch.lifecycle;

/**
 * {@link LiveData} which publicly exposes {@link #setValue(T)} and {@link #postValue(T)} method.
 *
 * @param <T> The type of data hold by this instance
 */
@SuppressWarnings("WeakerAccess")
public class MutableLiveData<T> extends LiveData<T> {
    @Override
    public void postValue(T value) {
        super.postValue(value);
    }

    @Override
    public void setValue(T value) {
        super.setValue(value);
    }
}

回答:


177

ドキュメントに基づいて:

setValue()

値を設定します。アクティブなオブザーバーがいる場合、値はそれらにディスパッチされます。このメソッドは、メインスレッドから呼び出す必要があります。

postValue()

指定された値を設定するためにタスクをメインスレッドにポストします。メインスレッドがポストされたタスクを実行する前にこのメソッドを複数回呼び出した場合、最後の値のみがディスパッチされます。

要約すると、主な違いは次のとおりです。

setValue()メソッドはメインスレッドから呼び出す必要があります。ただし、バックグラウンドスレッドから値を設定する必要がある場合は、をpostValue()使用する必要があります。


「最後の値のみがディスパッチされます」。コードを読んでそれを確認することはできません。したがって、最初のスレッドがpostValue()内の同期された内部ブロックにヒットしようとすると、次のCPUウィンドウが別の値をポストしているスレッド2に渡される可能性があります。スレッド2は同期されたブロックを完了でき、スケジューラは最初のスレッドに自身を実行するためのウィンドウを提供します。これで、スレッド2がすでに書き込んだものを上書きします。これは可能ですか?
stdout

93

上記の答えはすべて正しいです。しかし、もう1つ重要な違いがあります。あなたが呼び出す場合はpostValue()何もオブザーバーを持っていないフィールド上で、あなたが呼び出すことをした後getValue()、あなたがに設定された値を受け取りませんpostValue()。したがって、オブザーバーなしでバックグラウンドスレッドで作業する場合は注意してください。


3
私が三重に賛成できればいいのに!これに基づいて、setValue()可能であれば使用し、必要な場合にのみ「postValue()」を慎重に使用するのが最善のようです。ありがとう
jungledev 2018

1
いいえ、ここに「最善の」方法はありません。バックグラウンドスレッドからLiveDataを操作する場合は、postValueを使用する必要があります。また、ライフサイクルコンポーネントの最新バージョンでは修正されています...おそらく。
w201

「また、ライフサイクルコンポーネントの最新バージョンでは修正された...おそらく。」これについてもっと情報がありますか?おかげで
クリス・ネヴィル

1
私はいくつかのテストを行いましたが、libの最新バージョンでは、すべてが正常に機能しているようです。
w201

上記の具体的なコードを見せていただけますか?ViewModelの場合、私はのように実装しましたnoObserveLiveData.postValue("sample")。アクティビティの場合、getValueのように使用しviewModel.noObserveLiveData.getValueた場合、つまり、postValue()( "sample")で設定した値ではないのですか?
kwmt

13

setValue()呼び出し元スレッドから直接呼び出され、同期的にオブザーバーに通知し、LiveData値を即座に変更します。MainThreadからのみ呼び出すことができます。
postValue()このような内部で使用するnew Handler(Looper.mainLooper()).post(() -> setValue())ため、MainThread で実行setValueHandlerれます。任意のスレッドから呼び出すことができます。


10

setValue()

値を設定します。アクティブなオブザーバーがいる場合、値はそれらにディスパッチされます。

このメソッドは、メインスレッドから呼び出す必要があります

postValue

バックグラウンドスレッドから値を設定する必要がある場合は、 postValue(Object)

指定された値を設定するためにタスクをメインスレッドにポストします。

メインスレッドがポストされたタスクを実行する前にこのメソッドを複数回呼び出した場合、最後の値のみがディスパッチされます。


5

これは上記の問題に対する直接の回答ではありません。Sagarw201からの回答は素晴らしいです。しかし、MutableLiveDataのViewModelsで使用する簡単な経験則は次のとおりです。

private boolean isMainThread() {
    return Looper.myLooper() == Looper.getMainLooper();
}

private MutableLiveData<Boolean> mutVal = new MutableLiveData<>(false);
public LiveData<Boolean> getMutVal() { return this.mutVal;  }
public void setMutVal(boolean val) {
    if (isMainThread()) mutVal.setValue(val);
    else mutVal.postValue(val);
}

mutVal希望の値に置き換えます。


いいですね、これが好きです。Kotlinで、スマートアップデートをカプセル化する拡張機能を作成しました。これにより、アプリ全体での多数の値の更新が1つの一貫した呼び出しになります。
19Craig

4

setValue()メソッドはメインスレッドから呼び出す必要があります。バックグラウンドスレッドから値を設定する必要がある場合は、を使用できますpostValue()

詳細はこちら


0

このアプリでは、アクティビティ/画面の複数のビューのデータを含む単一のLiveDataを使用しました。基本的に、N個のビューのN個のデータセット。postDataの設計方法が原因で、少し問題が発生しました。そして、どのビューを更新する必要があるかをビューに伝えるLDの状態オブジェクトがあります。

LDは次のようになります。

LD {
   state (view_1, view_2, view_3 …),
   model_that_contains_data_of_all_views
}

1つのイベントが発生したときに更新する必要があったビュー(view_1とview_2)がいくつかあります。つまり、イベントが発生したときに同時に通知を受ける必要があります。だから、私は呼んだ:

postData(LD(view_1, data))
postData(LD(view_2, data)

これは、私たちが知っている理由で機能しません。

私が理解したことは、基本的に1つのLDは1つのビューのみを表す必要があるということです。その場合、postData()を続けて2回呼び出す必要はありません。あなたが呼び出したとしても、postDataがあなたのためにそれを処理する方法はあなたが期待するものでもあります(あなたのために最新のデータを表示します)。すべてが適切に配置されます。

1つのLD-> 1つのビュー。完璧な

1つのLD- >複数のビュー奇妙な振る舞い

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.