Mengamati LiveData dari ViewModel

91

Saya memiliki kelas terpisah tempat saya menangani pengambilan data (khususnya Firebase) dan saya biasanya mengembalikan objek LiveData darinya dan memperbaruinya secara asinkron. Sekarang saya ingin memiliki data yang dikembalikan disimpan dalam ViewModel, tetapi masalahnya adalah untuk mendapatkan nilai tersebut, saya perlu mengamati objek LiveData yang dikembalikan dari kelas pengambilan data saya. Metode observasi memerlukan objek LifecycleOwner sebagai parameter pertama, tetapi saya jelas tidak memilikinya di dalam ViewModel saya dan saya tahu saya tidak seharusnya menyimpan referensi ke Aktivitas / Fragmen di dalam ViewModel. Apa yang harus saya lakukan?

Vuk Bibic
sumber

Jawaban:

38

Dalam posting blog ini oleh pengembang Google Jose Alcérreca, disarankan untuk menggunakan transformasi dalam kasus ini (lihat paragraf "LiveData di repositori") karena ViewModel tidak boleh memiliki referensi apa pun yang terkait dengan View(Aktivitas, Konteks, dll.) Karena membuatnya sulit untuk menguji.

guglhupf.dll
sumber
sudahkah Anda berhasil membuat Transformasi bekerja untuk Anda? Acara saya tidak berfungsi
romaneso
23
Transformasi sendiri tidak berfungsi, karena kode apa pun yang Anda tulis dalam transformasi hanya dilampirkan untuk dijalankan ketika beberapa entitas mengamati transformasi .
orbitbot
5
Saya tidak tahu mengapa ini adalah jawaban yang direkomendasikan, tidak ada hubungannya dengan pertanyaan tersebut. 2 tahun kemudian, dan kami masih belum tahu bagaimana mengamati perubahan data repositori dalam model tampilan kami.
Andrew
24

Dalam dokumentasi ViewModel

Namun objek ViewModel tidak boleh mengamati perubahan pada observable yang sadar siklus proses, seperti objek LiveData.

Cara lain adalah agar data mengimplementasikan RxJava daripada LiveData, maka tidak akan ada manfaatnya karena peka terhadap siklus proses.

Dalam contoh todo-mvvm-live-kotlin google , ini menggunakan callback tanpa LiveData di ViewModel.

Saya menduga jika Anda ingin mematuhi seluruh gagasan tentang lifecycle-ware, kita perlu memindahkan kode observasi di Activity / Fragment. Lain, kita bisa menggunakan callback atau RxJava di ViewModel.

Kompromi lain adalah mengimplementasikan MediatorLiveData (atau Transformasi) dan mengamati (meletakkan logika Anda di sini) di ViewModel. Perhatikan bahwa pengamat MediatorLiveData tidak akan memicu (sama seperti Transformasi) kecuali jika diamati di Activity / Fragment. Apa yang kita lakukan adalah kita meletakkan observasi kosong di Activity / Fragment, di mana pekerjaan sebenarnya sebenarnya dilakukan di ViewModel.

// 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: Saya membaca ViewModels dan LiveData: Patterns + AntiPatterns yang menyarankan Transformasi itu. Saya tidak berpikir itu berfungsi kecuali LiveData diamati (yang mungkin mengharuskannya dilakukan di Activity / Fragment).

Desmond Lua
sumber
2
Apakah ada yang berubah dalam hal ini? Atau RX, callback atau observasi kosong hanyalah solusi?
qbait
2
Adakah solusi untuk menghilangkan pengamatan kosong ini?
Ehsan Mashhadi
1
Mungkin menggunakan Flow ( mLiveData.asFlow()) atau observeForever.
Machado
Solusi aliran tampaknya berfungsi jika Anda tidak ingin memiliki / Anda tidak memerlukan logika pengamat di Fragmen
adek111
14

Saya rasa Anda dapat menggunakan observForever yang tidak memerlukan antarmuka pemilik siklus hidup dan Anda dapat mengamati hasil dari viewmodel

siddharth.dll
sumber
2
ini sepertinya jawaban yang benar bagi saya terutama bahwa dalam dokumen tentang ViewModel.onCleared () dikatakan: "Ini berguna ketika ViewModel mengamati beberapa data dan Anda perlu menghapus langganan ini untuk mencegah kebocoran ViewModel ini."
Yosef
2
Maaf tapiCannot invoke observeForever on a background thread
Boken
1
Sepertinya itu cukup sah. Meskipun seseorang harus menyimpan pengamat di bidang viewModel dan berhenti berlangganan di onCleared. Mengenai utas latar belakang - amati dari utas utama, itu saja.
Kirill Starostin
@Boken Anda dapat memaksa observeForeveruntuk dipanggil dari utama melaluiGlobalScope.launch(Dispatchers.Main) { myvm.observeForever() }
rmirabelle
4

Gunakan coroutine Kotlin dengan komponen Arsitektur.

Anda bisa menggunakan liveDatafungsi builder untuk memanggil suatu suspendfungsi, menyajikan hasilnya sebagai LiveDataobjek.

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

Anda juga dapat memancarkan beberapa nilai dari blok tersebut. Setiap emit()panggilan menangguhkan eksekusi blok hingga LiveDatanilainya ditetapkan pada utas utama.

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

Dalam konfigurasi gradle Anda, gunakan androidx.lifecycle:lifecycle-livedata-ktx:2.2.0atau lebih tinggi.

Ada juga artikel tentang itu.

Pembaruan : Juga dimungkinkan untuk mengubah LiveData<YourData>file Dao interface. Anda perlu menambahkan suspendkata kunci ke fungsi:

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

dan ViewModelAnda harus mendapatkannya secara asinkron seperti itu:

viewModelScope.launch(Dispatchers.IO) {
    allData = dao.getAll()
    // It's also possible to sync other data here
}
Psijic
sumber