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?
91
Jawaban:
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.sumber
Dalam dokumentasi ViewModel
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).
sumber
mLiveData.asFlow()
) atauobserveForever
.Saya rasa Anda dapat menggunakan observForever yang tidak memerlukan antarmuka pemilik siklus hidup dan Anda dapat mengamati hasil dari viewmodel
sumber
Cannot invoke observeForever on a background thread
onCleared
. Mengenai utas latar belakang - amati dari utas utama, itu saja.observeForever
untuk dipanggil dari utama melaluiGlobalScope.launch(Dispatchers.Main) { myvm.observeForever() }
Gunakan coroutine Kotlin dengan komponen Arsitektur.
Anda bisa menggunakan
liveData
fungsi builder untuk memanggil suatususpend
fungsi, menyajikan hasilnya sebagaiLiveData
objek.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 hinggaLiveData
nilainya 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.0
atau lebih tinggi.Ada juga artikel tentang itu.
Pembaruan : Juga dimungkinkan untuk mengubah
LiveData<YourData>
fileDao
interface
. Anda perlu menambahkansuspend
kata kunci ke fungsi:@Query("SELECT * FROM the_table") suspend fun getAll(): List<YourData>
dan
ViewModel
Anda harus mendapatkannya secara asinkron seperti itu:viewModelScope.launch(Dispatchers.IO) { allData = dao.getAll() // It's also possible to sync other data here }
sumber