Mengapa ada subkelas MutableLiveData terpisah dari LiveData?

96

Sepertinya MutableLiveDataberbeda dari LiveDatahanya dengan membuat setValue()dan postValue()metode publik, sedangkan di dalamnya LiveDatadilindungi.

Apa alasan untuk membuat kelas terpisah untuk perubahan ini dan tidak hanya mendefinisikan metode tersebut sebagai publik LiveDataitu sendiri?

Secara umum, apakah bentuk pewarisan semacam itu (meningkatkan visibilitas metode tertentu menjadi satu-satunya perubahan) merupakan praktik yang terkenal dan beberapa skenario apa yang mungkin berguna (dengan asumsi kita memiliki akses ke semua kode)?

Alexander Kulyakhtin
sumber
10
itu adalah keputusan desain. LiveDatatidak dapat diubah, karena klien tidak dapat mengubah status internal, oleh karena itu aman utas
Blackbelt

Jawaban:

138

Di LiveData - Dokumentasi Pengembang Android , Anda dapat melihat bahwa untuk LiveData, setValue()& postValue()metode tidak bersifat publik.

Padahal, di MutableLiveData - Dokumentasi Pengembang Android , Anda dapat melihat bahwa, MutableLiveDatameluas LiveDatasecara internal dan juga dua metode keajaiban LiveDatayang umum tersedia di ini dan mereka setValue()& postValue().

setValue(): mengatur nilai dan mengirimkan nilai ke semua pengamat aktif, harus dipanggil dari utas utama .

postValue(): memposting tugas ke utas utama untuk mengganti nilai yang ditetapkan setValue(), harus dipanggil dari utas latar belakang .

Jadi, tidakLiveData bisa diubah . MutableLiveDataadalah LiveDatayang bisa berubah & aman utas .

Sneh Pandya
sumber
36
LiveData tidak benar-benar tidak dapat diubah, hanya saja LiveData tidak dapat dimodifikasi di luar kelas ViewModel. Kelas ViewModel dapat memodifikasinya sesuka hati (mis. ViewModel pengatur waktu). Anda akan menggunakan MutableLiveData jika Anda ingin mengubahnya di luar kelas ViewModel.
Elliptica
2
Mari kita ambil skenario ini, sebuah aplikasi dengan pola repositori (Server + Room) di mana Room adalah Sumber Tunggal kebenaran. Aplikasi hanya mendapatkan data dari Room, sedangkan Room mendapatkan pembaruannya dari server. Apakah mutableLiveData harus digunakan karena data dari server memperbarui Room, atau LiveData dapat digunakan?
Dr4ke the b4dass
5
LiveData bersifat abstrak, jadi Anda tidak dapat membuat objek LiveData secara langsung tanpa memperluasnya. MutableLiveData memperluas LiveData.
Serdar Samancıoğlu
1
Tautan ke LiveData dan MutableLiveData langsung ke dokumentasi yang tidak digunakan lagi. Mengapa ketika saya menyarankan pengeditan dengan tautan yang sebenarnya, itu ditolak?
Daniel
1
@Daniel tidak yakin mengapa ditolak oleh pengulas lain dalam antrean tinjauan. Saya telah menyetujui perubahan tersebut, terima kasih! :)
Sneh Pandya
10

Ini adalah keseluruhan MutableLiveData.javafile:

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);
    }
}

Jadi ya, perbedaan hanya datang dari pembuatan postValuedan setValuepublik.

Satu kasus penggunaan yang bisa saya ingat adalah untuk enkapsulasi menggunakan Backing Property di Kotlin. Anda dapat mengekspos LiveDataFragmen / Aktivitas (Pengontrol UI) meskipun Anda dapat memiliki MutableLiveDatamanipulasi di ViewModelkelas Anda .

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

Dengan cara ini Pengontrol UI Anda hanya dapat mengamati nilai tanpa dapat mengeditnya. Jelas, Pengontrol UI Anda dapat mengedit nilai menggunakan metode publik TempViewModelsejenis incrementCount().

Catatan : Untuk memperjelas kebingungan yang bisa berubah / tidak berubah -

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
}
Ananth
sumber
Apa _score?
IgorGanapolsky
0

MutableLiveData diturunkan dari LiveData. Metode yang dilindungi LiveData hanya dapat ditangani oleh diri sendiri atau subkelas. Jadi dalam hal ini MutableLiveData sebagai sub kelas LiveData dapat mengakses metode yang dilindungi ini.

Apa yang ingin Anda lakukan adalah mengamati pada sebuah contoh dan melihat apakah ada perubahan. Tetapi pada saat yang sama Anda tidak ingin ada "orang luar" yang mengubah contoh yang Anda amati. Dalam arti tertentu ini menciptakan masalah, karena Anda ingin memiliki objek yang dan dapat diubah, untuk memperbarui status baru, dan tidak dapat diubah, untuk memastikan tidak ada orang yang tidak dapat memperbarui contoh ini. Kedua fitur ini saling bertentangan tetapi dapat diselesaikan dengan membuat lapisan tambahan.

Jadi yang Anda lakukan adalah memperluas kelas Anda, LiveData, dengan kelas yang dapat mengakses metodenya. Sub lapisan, dalam hal ini MutableLiveData, dapat mengakses metode yang dilindungi dari induknya (/ super).

Sekarang Anda mulai membuat instance, dan membuat instance pengamat Anda dari MutableLiveData. Pada saat yang sama Anda membuat instance LiveData yang merujuk ke instance yang sama ini. Karena MutableLiveData memperluas LiveData, setiap instance MutableLiveData adalah objek LiveData dan oleh karena itu dapat direferensikan oleh variabel LiveData.

Sekarang triknya hampir selesai. Anda hanya mengekspos instance LiveData, tidak ada yang dapat menggunakan metode terlindungnya, juga tidak dapat mentransmisikannya ke supernya (mungkin pada waktu kompilasi, tetapi tidak akan berjalan: RunTime error). Dan Anda membuat instance subkelas yang sebenarnya tetap pribadi, jadi itu hanya dapat diubah oleh mereka yang memiliki instance, menggunakan metode instance.

//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(.....)

Sekarang kelas super memberi tahu jika ada perubahan yang diterapkan.

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

Blockquote Secara umum, apakah bentuk pewarisan semacam itu (meningkatkan visibilitas metode tertentu menjadi satu-satunya perubahan) merupakan praktik yang terkenal dan skenario apa yang mungkin berguna (dengan asumsi kita memiliki akses ke semua kode)?

Ya, ini cukup terkenal dan ini dijelaskan di atas adalah skenario umum. Hapus pola pengamat, dan buat dalam bentuk set / get akan mendapatkan banyak keuntungan darinya. Tergantung dari mana Anda menerapkannya, tidak ada aturan emas pada akhirnya.

Khamaseen
sumber