ViewModelからのLiveDataの観察


95

データフェッチ(具体的にはFirebase)を処理する別のクラスがあり、通常はそこからLiveDataオブジェクトを返し、非同期で更新します。返されたデータをViewModelに保存したいのですが、問題は、その値を取得するために、データフェッチクラスから返されたLiveDataオブジェクトを監視する必要があることです。監視メソッドには最初のパラメーターとしてLifecycleOwnerオブジェクトが必要でしたが、ViewModel内にそれがないことは明らかであり、ViewModel内のActivity / Fragmentへの参照を保持することになっていないことはわかっています。私は何をすべきか?


回答:


39

、このブログの記事、Googleの開発者ホセAlcérrecaによってため(段落「リポジトリでLiveData」を参照)、この場合の変換を使用することが推奨されてViewModelにはに関連するすべての参照を保持べきではないView、それは難しいそれを作ったので(活動、コンテキストなど)テストする。


トランスフォーメーションを機能させることができましたか?私のイベントは機能していません
romaneso 2017

26
変換で記述したコードは、一部のエンティティが変換を監視し場合にのみ実行されるようにアタッチされているため、変換自体は機能しません。
orbitbot 2018

9
なぜこれが推奨される答えなのかわかりません。質問とは何の関係もありません。2年後、ビューモデルでリポジトリデータの変更を監視する方法がまだわかりません。
アンドリュー

26

のViewModelドキュメント

ただし、ViewModelオブジェクトは、LiveDataオブジェクトなどのライフサイクル対応の監視対象への変更を監視してはなりません。

もう1つの方法は、データがLiveDataではなくRxJavaを実装することです。そうすると、ライフサイクルを認識するという利点がなくなります。

todo-mvvm-live-kotlinのGoogleサンプルでは、ViewModelでLiveDataなしのコールバックを使用しています。

ライフサイクルウェアであるという考え全体に準拠したい場合は、アクティビティ/フラグメントで観測コードを移動する必要があると思います。それ以外の場合は、ViewModelでコールバックまたはRxJavaを使用できます。

もう1つの妥協点は、MediatorLiveData(またはTransformations)を実装し、ViewModelで監視(ここにロジックを配置)することです。MediatorLiveDataオブザーバーは、Activity / Fragmentで監視されない限り、トリガーされないことに注意してください(Transformationsと同じ)。実際の作業はViewModelで実際に行われる、Activity / Fragmentに空白のオブザーブを配置します。

// ViewModel
fun start(id : Long) : LiveData<User>? {
    val liveData = MediatorLiveData<User>()
    liveData.addSource(dataSource.getById(id), Observer {
        if (it != null) {
            // put your logic here
        }
    })
}

// Activity/Fragment
viewModel.start(id)?.observe(this, Observer {
    // blank observe here
})

PS:ViewModelsとLiveData:Patterns + AntiPatternsを読みました。これは、変換を示唆しています。LiveDataが観察されない限り、機能するとは思いません(おそらく、Activity / Fragmentで実行する必要があります)。


2
この点で何か変化はありましたか?または、RX、コールバック、または空白の監視は解決策にすぎませんか?
qbait 2018

2
これらの空白の観察を取り除くための解決策はありますか?
EhsanMashhadi18年

1
たぶんフロー(mLiveData.asFlow())またはを使用しobserveForeverます。
マチャド

フローソリューションは、フラグメントにオブザーバーロジックが必要ない/必要ない場合に機能するようです
adek1 1120年

14

ライフサイクル所有者インターフェースを必要としないobserveForeverを使用でき、ビューモデルから結果を観察できると思います


2
これは私にとって正しい答えのようです。特に、ViewModel.onCleared()に関するドキュメントでは、「ViewModelが一部のデータを監視し、このViewModelのリークを防ぐためにこのサブスクリプションをクリアする必要がある場合に役立ちます」と述べられています。
Yosef

2
申し訳ありませんがCannot invoke observeForever on a background thread
冒険

1
それはかなり正当なようです。ただし、viewModelフィールドにオブザーバーを保存し、でサブスクライブを解除する必要がありonClearedます。バックグラウンドスレッドについては、メインスレッドから観察してください。それだけです。
KirillStarostin19年

@BokenobserveForeverメイン経由で強制的に呼び出すことができますGlobalScope.launch(Dispatchers.Main) { myvm.observeForever() }
rmir​​abelle 2010年

5

アーキテクチャコンポーネントでKotlinコルーチンを使用します。

liveDataビルダー関数を使用して関数を呼び出しsuspend、結果をLiveDataオブジェクトとして提供できます。

val user: LiveData<User> = liveData {
    val data = database.loadUser() // loadUser is a suspend function.
    emit(data)
}

ブロックから複数の値を出力することもできます。各emit()呼び出しは、LiveData値がメインスレッドに設定されるまで、ブロックの実行を一時停止します。

val user: LiveData<Result> = liveData {
    emit(Result.loading())
    try {
        emit(Result.success(fetchUser()))
    } catch(ioException: Exception) {
        emit(Result.error(ioException))
    }
}

Gradle構成で、androidx.lifecycle:lifecycle-livedata-ktx:2.2.0またはそれ以上を使用します。

それについての記事もあります。

更新:で変更LiveData<YourData>することも可能Dao interfaceです。suspend関数にキーワードを追加する必要があります。

@Query("SELECT * FROM the_table")
suspend fun getAll(): List<YourData>

そして、ViewModelあなたはそれを次のように非同期的に取得する必要があります:

viewModelScope.launch(Dispatchers.IO) {
    allData = dao.getAll()
    // It's also possible to sync other data here
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.