LiveDataの個別のMutableLiveDataサブクラスがあるのはなぜですか?


96

とメソッドを公開することだけMutableLiveDataが異なるように見えますが、それらは保護されています。LiveDatasetValue()postValue()LiveData

この変更のために別のクラスを作成し、それらのメソッドLiveData自体を単にパブリックとして定義しない理由は何ですか?

一般的に言って、そのような形式の継承(特定のメソッドの可視性を高めることが唯一の変更である)はよく知られた方法であり、それが役立つ可能性のあるいくつかのシナリオは何ですか(すべてのコードにアクセスできると仮定)?


10
それは設計上の決定です。LiveDataクライアントは内部状態を変更できないため、は不変です。したがって、スレッドセーフです
Blackbelt 2017年

回答:


137

LiveData - Androidの開発者用ドキュメント、あなたがのためにそれを見ることができるLiveDatasetValue()postValue()の方法は、公開されません。

一方、MutableLiveData-Android Developer Documentationでは、MutableLiveDataLiveData内部的に拡張されており、の2つの魔法のメソッドがこれLiveData公開されており、setValue()postValue()です。

setValue():値を設定し、すべてのアクティブなオブザーバーに値をディスパッチしますメインスレッドから呼び出す必要があります

postValue():タスクをメインスレッドに投稿して、によって設定された値をオーバーライドします。バックグラウンドスレッドsetValue()から呼び出す必要があります

したがって、LiveData不変です。MutableLiveDataLiveDataある可変スレッドセーフ


36
LiveDataが不変であるというわけではなく、ViewModelクラスの外部で変更できないというだけです。ViewModelクラスは、必要に応じて変更できます(タイマーViewModelなど)。ViewModelクラスの外部で変更する場合は、MutableLiveDataを使用します。
Elliptica

2
このシナリオを考えてみましょう。リポジトリパターン(サーバー+ルーム)を備えたアプリで、ルームは信頼できる唯一の情報源です。アプリはRoomからのみデータを取得しますが、Roomはサーバーから更新を取得します。サーバー更新ルームからのデータ、またはLiveDataを使用できるため、mutableLiveDataを使用する必要がありますか?
Dr4ke the b4dass 2018年

5
LiveDataは抽象的であるため、LiveDataオブジェクトを拡張せずに直接作成することはできません。MutableLiveDataはLiveDataを拡張します。
セルダルSamancıoğlu

1
LiveDataおよびMutableLiveDataへのリンクは、非推奨のドキュメントに直接リンクしています。実際のリンクを使用して編集を提案したときに拒否されたのはなぜですか?
ダニエル

1
@Danielは、レビューキュー内の他のレビュー担当者によって拒否された理由がわかりません。変更を承認しました、ありがとう!:)
SnehPandya19年

10

これはMutableLiveData.javaファイル全体です:

package androidx.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);
    }
}

そうです、違いは作ることpostValuesetValue公開することによってのみもたらされます。

私は私の頭のオフ思い出すことができることの一つのユースケースを使用してカプセル化のためであるバッキングプロパティをKotlinに。クラスでの操作LiveDataが可能であっても、フラグメント/アクティビティ(UIコントローラー)に公開できMutableLiveDataますViewModel

    class TempViewModel : ViewModel() {
        ...
        private val _count = MutableLiveData<Int>()
        val count: LiveData<Int>
            get() = _count
        public fun incrementCount() = _count.value?.plus(1)
        ...
    }

このように、UIコントローラーは、値を編集することなく、値を監視することしかできません。明らかに、UIコントローラーはのTempViewModelようなパブリックメソッドを使用して値を編集できます incrementCount()

:可変/不変の混乱を明確にするために-

data class User(var name: String, var age: Int)

class DemoLiveData: LiveData<User>()

var demoLiveData: LiveData<User>? = DemoLiveData()

fun main() {
    demoLiveData?.value = User("Name", 23) // ERROR
    demoLiveData?.value?.name = "Name" // NO ERROR
    demoLiveData?.value?.age = 23  // NO ERROR
}

_scoreですか?
IgorGanapolsky

0

MutableLiveDataはLiveDataから拡張されています。LiveDataの保護されたメソッドは、selfまたはサブクラスによってのみアドレス指定できます。したがって、この場合、LiveDataのサブクラスであるMutableLiveDataは、これらの保護されたメソッドにアクセスできます。

あなたがしたいことは、インスタンスを観察し、変更があるかどうかを確認することです。しかし同時に、あなたは「部外者」があなたが観察しているそのインスタンスを変更することを望まない。ある意味で、これは問題を引き起こします。変更可能なオブジェクトが必要なため、変更できない新しいステータスを更新して、このインスタンスを更新できない人がいないことを確認します。これらの2つの機能は互いに競合しますが、追加のレイヤーを作成することで解決できます。

つまり、メソッドにアクセスできるクラスを使用して、クラスLiveDataを拡張する必要があります。サブレイヤー(この場合はMutableLiveData)は、その親(/ super)の保護されたメソッドにアクセスできます。

次に、インスタンスの作成を開始し、MutableLiveDataのオブザーバーインスタンスを作成します。同時に、この同じインスタンスを参照するLiveDataインスタンスを作成します。MutableLiveDataはLiveDataを拡張するため、MutableLiveDataインスタンスはすべてLiveDataオブジェクトであり、LiveData変数から参照できます。

これで、トリックはほぼ完了しました。LiveDataインスタンスのみを公開し、保護されたメソッドを使用することも、スーパーにキャストすることもできません(コンパイル時に実行される可能性がありますが、実行されません:ランタイムエラー)。また、実際のサブクラスインスタンスをプライベートに保つため、インスタンスのメソッドを使用して、インスタンスを所有している人だけが変更できます。

//create instance of the sub class and keep this private
private val _name: MutableLiveData<String> = MutableLiveData<String>()
//create an instance of the super class referring to the same instance
val name: LiveData<String> = _name
//assign observer to the super class, being unable to change it
name.value.observe(.....)

これで、スーパークラスは変更が適用されたときに通知します。

//change the instance by using the sub class
_name.postValue(...)
//or _name.setValue(...)

ブロッククォート一般的に言って、そのような形式の継承(特定のメソッドの可視性を高めることが唯一の変更である)はよく知られた慣行であり、それが役立つ可能性のあるいくつかのシナリオは何ですか(すべてのコードにアクセスできると仮定)?

はい、それは非常によく知られており、上記のこれは一般的なシナリオです。オブザーバーパターンを削除し、それをset / get形式にするだけで、同じように多くのメリットが得られます。実装する場所によっては、最終的にゴールデンルールはありません。

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