Tại sao có một lớp con MutableLiveData riêng biệt của LiveData?


96

Có vẻ như chỉ MutableLiveDatakhác ở chỗ LiveDatachỉ đặt phương thức setValue()và ở chế độ postValue()công khai, trong khi LiveDatachúng được bảo vệ.

Một số lý do để tạo một lớp riêng biệt cho sự thay đổi này và không chỉ đơn giản xác định các phương thức đó là public trong LiveDatachính nó?

Nói chung, một hình thức kế thừa như vậy (tăng khả năng hiển thị của một số phương pháp là thay đổi duy nhất) có phải là một thực tiễn nổi tiếng và một số tình huống mà nó có thể hữu ích là gì (giả sử chúng ta có quyền truy cập vào tất cả mã)?


10
nó là một quyết định thiết kế. LiveDatalà không thay đổi, kể từ khi khách hàng không có thể thay đổi trạng thái nội bộ, do đó thread-safe
Blackbelt

Câu trả lời:


137

Trong LiveData - Tài liệu cho nhà phát triển Android , bạn có thể thấy rằng đối LiveData, setValue()& postValue()phương pháp này không được công khai.

Trong khi đó, trong MutableLiveData - Tài liệu dành cho nhà phát triển Android , bạn có thể thấy điều đó, MutableLiveDatamở rộng LiveDatatrong nội bộ và cũng có hai phương pháp ma thuật LiveDatađược công bố công khai trong này và chúng setValue()& postValue().

setValue(): đặt giá trị và gửi giá trị đến tất cả các quan sát viên đang hoạt động, phải được gọi từ luồng chính .

postValue(): đăng một tác vụ lên luồng chính để ghi đè giá trị được đặt bởi setValue(), phải được gọi từ luồng nền .

Vì vậy, LiveDatabất biến . MutableLiveDataLiveDatađó là có thể thay đổi & thread-safe .


36
Không hẳn LiveData là bất biến, chỉ là nó không thể sửa đổi bên ngoài lớp ViewModel. Lớp ViewModel có thể sửa đổi nó theo cách nào nó muốn (ví dụ: ViewModel hẹn giờ). Bạn sẽ sử dụng MutableLiveData nếu bạn muốn sửa đổi nó bên ngoài lớp ViewModel.
Elliptica

2
Hãy xem kịch bản này, một ứng dụng có mô hình kho lưu trữ (Máy chủ + Phòng) trong đó Phòng là Nguồn duy nhất của sự thật. Ứng dụng chỉ nhận dữ liệu từ Room, trong khi Room nhận bản cập nhật từ máy chủ. Có phải là phải sử dụng mutableLiveData vì dữ liệu từ phòng cập nhật máy chủ hoặc LiveData có thể được sử dụng không?
Dr4ke the b4dass

5
LiveData là trừu tượng, vì vậy bạn không thể tạo trực tiếp một đối tượng LiveData mà không mở rộng nó. MutableLiveData mở rộng LiveData.
Serdar Samancıoğlu

1
Các liên kết tới LiveData và MutableLiveData trực tiếp đến tài liệu không dùng nữa. Tại sao khi tôi đề xuất một chỉnh sửa với các liên kết thực tế, nó lại bị từ chối?
Daniel

1
@Daniel không chắc tại sao nó bị từ chối bởi những người đánh giá khác trong hàng đợi đánh giá. Tôi đã chấp thuận thay đổi, cảm ơn! :)
Sneh Pandya

10

Đây là toàn bộ MutableLiveData.javatệp:

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

Vì vậy, có, sự khác biệt chỉ đến bằng cách đưa ra postValuesetValuecông khai.

Một trường hợp sử dụng mà tôi có thể nhớ ra trong đầu là đóng gói bằng cách sử dụng Thuộc tính sao lưu trong Kotlin. Bạn có thể tiếp xúc LiveDatavới Phân đoạn / Hoạt động (Bộ điều khiển giao diện người dùng) của mình ngay cả khi bạn có thể MutableLiveDatathao tác trong ViewModellớp của mình .

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

Bằng cách này, Bộ điều khiển giao diện người dùng của bạn sẽ chỉ có thể quan sát các giá trị mà không thể chỉnh sửa chúng. Rõ ràng, Bộ điều khiển giao diện người dùng của bạn có thể chỉnh sửa các giá trị bằng cách sử dụng các phương pháp công khai TempViewModelnhư incrementCount().

Lưu ý : Để làm rõ sự nhầm lẫn có thể thay đổi / không thay đổi -

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
}

_scoregì?
IgorGanapolsky

0

MutableLiveData được mở rộng từ LiveData. Các phương thức được bảo vệ của LiveData chỉ có thể được giải quyết bởi lớp tự hoặc lớp con. Vì vậy, trong trường hợp này MutableLiveData là một lớp con của LiveData có thể truy cập các phương thức được bảo vệ này.

Điều bạn muốn làm là quan sát một phiên bản và xem liệu có bất kỳ thay đổi nào không. Nhưng đồng thời bạn không muốn bất kỳ "người ngoài" nào thay đổi trường hợp mà bạn đang quan sát. Theo một nghĩa nào đó, điều này tạo ra một vấn đề, vì bạn muốn có một đối tượng có thể thay đổi được, để cập nhật bất kỳ trạng thái mới nào và không thể thay đổi, để đảm bảo không ai không nên cập nhật trường hợp này. Hai tính năng này xung đột với nhau nhưng có thể được giải quyết bằng cách tạo thêm một lớp.

Vì vậy, những gì bạn làm là mở rộng lớp của bạn, LiveData, với một lớp có thể truy cập các phương thức của nó. Lớp con, trong trường hợp này là MutableLiveData, có thể truy cập các phương thức được bảo vệ của lớp cha (/ super) của nó.

Bây giờ bạn bắt đầu tạo các phiên bản và tạo phiên bản quan sát của MutableLiveData. Đồng thời, bạn tạo một cá thể LiveData đề cập đến cùng một cá thể này. Vì MutableLiveData mở rộng LiveData, bất kỳ cá thể MutableLiveData nào đều là đối tượng LiveData và do đó có thể được tham chiếu bởi một biến LiveData.

Bây giờ thủ thuật gần như đã hoàn thành. Bạn chỉ hiển thị cá thể LiveData, không ai có thể sử dụng các phương thức được bảo vệ của nó, cũng như không thể truyền nó đến siêu nó (có thể tại thời điểm biên dịch, nhưng nó sẽ không chạy: Lỗi RunTime). Và bạn giữ cá thể lớp con thực tế ở chế độ riêng tư, vì vậy nó chỉ có thể được thay đổi bởi những người sở hữu cá thể đó bằng cách sử dụng các phương thức của cá thể đó.

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

Bây giờ siêu lớp sẽ thông báo khi có bất kỳ thay đổi nào được áp dụng.

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

Blockquote Nói chung, một hình thức kế thừa như vậy (tăng khả năng hiển thị của một số phương pháp là thay đổi duy nhất) có phải là một thực tiễn nổi tiếng không và một số tình huống mà nó có thể hữu ích là gì (giả sử chúng ta có quyền truy cập vào tất cả mã)?

Vâng, nó khá nổi tiếng và điều này được mô tả ở trên là một kịch bản phổ biến. Loại bỏ mô hình quan sát và chỉ cần đặt nó trong một biểu mẫu set / get cũng sẽ được nhiều lợi ích từ nó. Tùy thuộc vào nơi bạn thực hiện nó, không có quy tắc vàng cuối cùng.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.