Quan sát dữ liệu trực tiếp từ ViewModel


91

Tôi có một lớp riêng trong đó tôi xử lý việc tìm nạp dữ liệu (cụ thể là Firebase) và tôi thường trả về các đối tượng LiveData từ nó và cập nhật chúng không đồng bộ. Bây giờ tôi muốn dữ liệu trả về được lưu trữ trong ViewModel, nhưng vấn đề là để có được giá trị nói trên, tôi cần quan sát đối tượng LiveData được trả về từ lớp tìm nạp dữ liệu của mình. Phương thức quan sát yêu cầu đối tượng LifecycleOwner làm tham số đầu tiên, nhưng rõ ràng tôi không có đối tượng đó bên trong ViewModel của mình và tôi biết tôi không được phép giữ tham chiếu đến Activity / Fragment bên trong ViewModel. Tôi nên làm gì?


Câu trả lời:


38

Trong bài đăng trên blog này của nhà phát triển Google, Jose Alcérreca, bạn nên sử dụng chuyển đổi trong trường hợp này (xem đoạn "Dữ liệu trực tiếp trong kho lưu trữ") vì ViewModel không nên chứa bất kỳ tham chiếu nào liên quan đến View(Hoạt động, Ngữ cảnh, v.v.) vì nó làm khó để kiểm tra.


bạn đã quản lý để chuyển đổi hoạt động cho bạn? Các sự kiện của tôi không hoạt động
romaneso

24
Các phép biến đổi tự nó không hoạt động, vì bất kỳ mã nào bạn viết trong phép biến đổi chỉ được đính kèm để chạy khi một thực thể nào đó quan sát thấy sự chuyển đổi .
fundbot

6
Tôi không biết tại sao đây là câu trả lời được đề xuất, nó không liên quan gì đến câu hỏi. 2 năm sau, và chúng tôi vẫn không biết cách quan sát những thay đổi về dữ liệu kho lưu trữ trong mô hình xem của chúng tôi.
Andrew

24

Trong tài liệu ViewModel

Tuy nhiên, các đối tượng ViewModel không bao giờ được quan sát các thay đổi đối với các đối tượng quan sát nhận biết vòng đời, chẳng hạn như các đối tượng LiveData.

Một cách khác là để dữ liệu triển khai RxJava thay vì LiveData, khi đó nó sẽ không có lợi ích khi nhận biết được vòng đời.

Trong mẫu todo-mvvm-live-kotlin của google , nó sử dụng lệnh gọi lại không có LiveData trong ViewModel.

Tôi đoán nếu bạn muốn tuân thủ toàn bộ ý tưởng về vòng đời, chúng ta cần di chuyển mã quan sát trong Activity / Fragment. Ngoài ra, chúng ta có thể sử dụng callback hoặc RxJava trong ViewModel.

Một thỏa hiệp khác là triển khai MediatorLiveData (hoặc Transformations) và quan sát (đặt logic của bạn ở đây) trong ViewModel. Lưu ý rằng trình quan sát MediatorLiveData sẽ không kích hoạt (giống như Biến đổi) trừ khi nó được quan sát trong Activity / Fragment. Những gì chúng tôi làm là đặt một quan sát trống trong Activity / Fragment, nơi công việc thực sự được thực hiện trong 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
})

Tái bút : Tôi đã đọc ViewModels và LiveData: Patterns + AntiPatterns gợi ý rằng Transformations. Tôi không nghĩ rằng nó hoạt động trừ khi LiveData được quan sát (có thể yêu cầu nó được thực hiện tại Activity / Fragment).


2
Có gì thay đổi trong vấn đề này không? Hoặc RX, gọi lại hoặc quan sát trống chỉ là giải pháp?
qbait

2
Bất kỳ giải pháp nào để thoát khỏi những quan sát trống?
Ehsan Mashhadi

1
Có thể sử dụng Flow ( mLiveData.asFlow()) hoặc observeForever.
Machado

Giải pháp luồng dường như hoạt động nếu bạn không muốn có / bạn không cần bất kỳ logic quan sát nào trong Fragment
adek111

14

Tôi nghĩ bạn có thể sử dụng ObserForever không yêu cầu giao diện chủ sở hữu vòng đời và bạn có thể quan sát kết quả từ mô hình xem


2
Đây có vẻ là câu trả lời phù hợp với tôi, đặc biệt là trong tài liệu về ViewModel.onCleared () có nói: "Sẽ rất hữu ích khi ViewModel quan sát một số dữ liệu và bạn cần xóa đăng ký này để ngăn chặn việc rò rỉ ViewModel này."
Yosef

2
Xin lỗi nhưngCannot invoke observeForever on a background thread
Boken

1
Điều đó có vẻ khá chính đáng. Mặc dù người ta phải lưu những người quan sát trong các trường viewModel và hủy đăng ký tại onCleared. Đối với luồng nền - hãy quan sát từ luồng chính, thế là xong.
Kirill Starostin

@Boken Bạn có thể buộc observeForeverđược gọi từ chính thông quaGlobalScope.launch(Dispatchers.Main) { myvm.observeForever() }
rmirabelle

4

Sử dụng Kotlin coroutines với các thành phần Kiến trúc.

Bạn có thể sử dụng liveDatahàm trình tạo để gọi một suspendhàm, cung cấp kết quả dưới dạng một LiveDatađối tượng.

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

Bạn cũng có thể phát ra nhiều giá trị từ khối. Mỗi emit()cuộc gọi sẽ tạm dừng việc thực thi khối cho đến khi LiveDatagiá trị được đặt trên luồng chính.

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

Trong cấu hình gradle của bạn, hãy sử dụng androidx.lifecycle:lifecycle-livedata-ktx:2.2.0hoặc cao hơn.

Cũng có một bài báo về nó.

Cập nhật : Ngoài ra, có thể thay đổi LiveData<YourData>trong Dao interface. Bạn cần thêm suspendtừ khóa vào hàm:

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

và trong trường hợp ViewModelbạn cần lấy nó không đồng bộ như vậy:

viewModelScope.launch(Dispatchers.IO) {
    allData = dao.getAll()
    // It's also possible to sync other data here
}
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.