Чому існує окремий підклас MutableLiveData LiveData?


96

Схоже, він MutableLiveDataвідрізняється від LiveDataлише тим, що робить setValue()і postValue()методи загальнодоступними, тоді як у LiveDataних вони захищені.

Які причини зробити окремий клас для цієї зміни, а не просто визначити ці методи як загальнодоступні LiveData?

Взагалі кажучи, чи є така форма успадкування (підвищення видимості деяких методів - єдиною зміною) загальновідомою практикою і які сценарії можуть бути корисними (за умови, що ми маємо доступ до всього коду)?


10
це дизайнерське рішення. LiveDataє незмінним, оскільки клієнт не може змінити внутрішній стан, отже, безпечний для потоків
Blackbelt

Відповіді:


138

У LiveData - Android документації розробника , ви можете побачити , що для LiveData, setValue()і postValue()методи не є публічними.

Тоді як у MutableLiveData - Документація розробника Android ви можете бачити, що це MutableLiveDataпоширюється LiveDataвнутрішньо, а також два магічні методи LiveDataє загальнодоступними в цьому, і вони setValue()& postValue().

setValue(): встановити значення та відправити значення для всіх активних спостерігачів, повинно викликатися з основного потоку .

postValue(): опублікувати завдання в основному потоці, щоб замінити значення, встановлене setValue(), повинно викликатися з фонового потоку .

Отже, LiveDataє незмінним . MutableLiveDataце LiveDataякий є змінним і безпечним для потоків .


37
Це насправді не те, що LiveData незмінна, просто вона не може бути змінена поза класом ViewModel. Клас ViewModel може змінювати його як завгодно (наприклад, таймер ViewModel). Ви б використовували MutableLiveData, якщо хочете змінити його поза класом ViewModel.
Elliptica

2
Візьмемо цей сценарій, додаток із шаблоном сховища (Сервер + Кімната), де Кімната є єдиним джерелом істини. Додаток отримує дані лише з Room, тоді як Room отримує оновлення з сервера. Чи потрібно використовувати mutableLiveData, оскільки можуть використовуватися дані із серверної кімнати оновлення або LiveData?
Dr4ke the b4dass

5
LiveData є абстрактним, тому ви не можете створити безпосередньо об’єкт LiveData без його розширення. MutableLiveData розширює LiveData.
Сердар Саманчжоглу

1
Посилання на LiveData та MutableLiveData спрямовують на застарілу документацію. Чому, коли я запропонував редагування з фактичними посиланнями, його було відхилено?
Даніель

1
@Daniel не впевнений, чому його відхилили інші рецензенти в черзі рецензій. Я схвалив зміни, дякую! :)
Sneh Pandya

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

Так що так, різниця виникає лише шляхом оприлюднення postValueта setValueоприлюднення.

Одним із варіантів використання, який я можу згадати з голови, є інкапсуляція з використанням Backing Property у Котліні. Ви можете переглянути LiveDataсвій фрагмент / активність (контролер інтерфейсу користувача), навіть незважаючи на те, що ви можете мати MutableLiveDataдля маніпуляцій у своєму ViewModelкласі.

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

Таким чином, ваш контролер інтерфейсу користувача зможе спостерігати значення лише без можливості редагувати їх. Очевидно, що ваш контролер інтерфейсу користувача може редагувати значення, використовуючи загальнодоступні методи, 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?
ІгорГанапольський

0

MutableLiveData поширюється на LiveData. До захищених методів LiveData можна звертатися лише за допомогою власних або підкласів. Тож у цьому випадку MutableLiveData, будучи підкласом LiveData, може отримати доступ до цих захищених методів.

Що ви хотіли б зробити, це спостерігати за екземпляром і перевіряти, чи є якісь зміни. Але в той же час ви не хочете, щоб "сторонні" змінювали той екземпляр, який ви спостерігаєте. У певному сенсі це створює проблему, оскільки ви хотіли б мати об’єкт, який є і змінним, для оновлення будь-якого нового статусу і не змінюється, щоб переконатися, що ніхто, хто не повинен, може оновити цей екземпляр. Ці дві функції конфліктують між собою, але їх можна вирішити, створивши додатковий шар.

Отже, що ви робите, це розширюєте свій клас LiveData класом, який може отримати доступ до своїх методів. Додатковий рівень, в даному випадку MutableLiveData, може отримати доступ до захищених методів свого батьківського (/ super).

Тепер ви починаєте створювати екземпляри та створюєте екземпляр спостерігача MutableLiveData. Одночасно ви створюєте екземпляр LiveData із посиланням на цей самий екземпляр. Оскільки MutableLiveData розширює LiveData, будь-який екземпляр MutableLiveData є об’єктом LiveData, і на нього може посилатися змінна LiveData.

Тепер фокус майже зроблений. Ви виставляєте лише екземпляр LiveData, ніхто не може використовувати його захищені методи, а також не може передати його супер (можливо, під час компіляції, але він не запускається: помилка RunTime). І ви зберігаєте фактичний екземпляр підкласу приватним, тому його можуть змінити лише ті, хто володіє екземпляром, використовуючи методи екземпляра.

//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, щоб настільки ж виграв від нього. Залежно від того, де ви це впровадите, в підсумку ніяких золотих правил.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.