ViewModel에서 LiveData 관찰


90

데이터 가져 오기 (특히 Firebase)를 처리하는 별도의 클래스가 있으며 일반적으로 여기에서 LiveData 개체를 반환하고 비동기 적으로 업데이트합니다. 이제 반환 된 데이터를 ViewModel에 저장하고 싶지만 문제는 해당 값을 얻으려면 데이터 가져 오기 클래스에서 반환 된 LiveData 개체를 관찰해야한다는 것입니다. 관찰 메서드에는 첫 번째 매개 변수로 LifecycleOwner 개체가 필요했지만 분명히 ViewModel 내부에는 해당 개체가 없으며 ViewModel 내부의 Activity / Fragment에 대한 참조를 유지하지 않아야한다는 것을 알고 있습니다. 어떻게해야합니까?


답변:


38

Google 개발자 인 Jose Alcérreca 의이 블로그 게시물 에서는 ViewModelView(활동, 컨텍스트 등) 과 관련된 참조를 보유하지 않아야 하기 때문에이 경우 변환을 사용하는 것이 좋습니다 ( "저장소의 LiveData"단락 참조 ). 테스트합니다.


Transformation이 당신을 위해 일하게 되었습니까? 내 이벤트가 작동하지 않습니다
romaneso

23
변환에서 작성한 코드는 어떤 엔터티가 변환을 관찰 할 때만 실행되도록 첨부되므로 변환 자체는 작동하지 않습니다 .
orbitbot

5
왜 이것이 권장되는 대답인지 모르겠습니다. 질문과 관련이 없습니다. 2 년이 지난 지금도 뷰 모델에서 리포지토리 데이터 변경을 관찰하는 방법을 모릅니다.
Andrew

24

에서 의 ViewModel의 문서

그러나 ViewModel 개체는 LiveData 개체와 같은 수명주기 인식 관찰 가능 항목의 변경 사항을 관찰해서는 안됩니다.

또 다른 방법은 데이터가 LiveData가 아닌 RxJava를 구현하는 것입니다. 그러면 수명주기를 인식하는 이점이 없습니다.

todo-mvvm-live-kotlin 의 Google 샘플에서는 ViewModel에서 LiveData 없이 콜백을 사용합니다.

라이프 사이클웨어라는 전체 아이디어를 준수하려면 Activity / Fragment에서 관찰 코드를 이동해야합니다. 그렇지 않으면 ViewModel에서 콜백 또는 RxJava를 사용할 수 있습니다.

또 다른 절충안은 MediatorLiveData (또는 Transformations)를 구현하고 ViewModel에서 관찰 (여기에 논리 입력)하는 것입니다. MediatorLiveData 관찰자는 Activity / Fragment에서 관찰되지 않는 한 트리거되지 않습니다 (변환과 동일). 우리가하는 일은 실제 작업이 ViewModel에서 실제로 수행되는 Activity / Fragment에 빈 관찰을 넣는 것입니다.

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

추신 : 나는 ViewModels 및 LiveData : Patterns + AntiPatterns읽었 으며 변환을 제안했습니다. LiveData가 관찰되지 않는 한 작동하지 않는다고 생각합니다 (아마도 Activity / Fragment에서 수행해야 함).


2
이와 관련하여 변경된 사항이 있습니까? 아니면 RX, 콜백 또는 공백 관찰이 유일한 해결책입니까?
qbait

2
이 공백을 제거하는 해결책은 무엇입니까?
Ehsan Mashhadi

1
Flow ( mLiveData.asFlow()) 또는 observeForever.
Machado

흐름 솔루션은 조각에 관찰자 논리가 필요하지 않거나 필요하지 않은 경우 작동하는 것 같습니다
adek111

14

라이프 사이클 소유자 인터페이스가 필요하지 않은 observeForever를 사용할 수 있고 viewmodel의 결과를 관찰 할 수 있다고 생각합니다.


2
이것은 특히 ViewModel.onCleared ()에 대한 문서에서 "ViewModel이 일부 데이터를 관찰 할 때 유용하며이 ViewModel의 누출을 방지하기 위해이 구독을 지워야 할 때 유용합니다."라는 정답 인 것 같습니다.
Yosef

2
죄송하지만Cannot invoke observeForever on a background thread
Boken

1
그것은 꽤 합법적 인 것 같습니다. viewModel 필드에 관찰자를 저장하고 onCleared. 백그라운드 스레드에 관해서는-메인 스레드에서 관찰하십시오.
Kirill Starostin 19

@Boken 당신은 강제 observeForever로 메인에서 호출 될 수 있습니다GlobalScope.launch(Dispatchers.Main) { myvm.observeForever() }
rmirabelle

4

아키텍처 구성 요소와 함께 Kotlin 코 루틴을 사용합니다.

liveData빌더 함수를 사용하여 함수를 호출 suspend하여 결과를 LiveData객체 로 제공 할 수 있습니다 .

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

블록에서 여러 값을 내보낼 수도 있습니다. 각 emit()호출은 LiveData값이 기본 스레드에 설정 될 때까지 블록 실행을 일시 중단합니다 .

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

gradle 구성에서 androidx.lifecycle:lifecycle-livedata-ktx:2.2.0이상을 사용하십시오 .

그것에 관한 기사 도 있습니다.

업데이트 : 또한 그것은 변경 가능 LiveData<YourData>Dao interface. suspend함수에 키워드 를 추가해야 합니다.

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

그리고 ViewModel당신 은 다음과 같이 비동기 적으로 가져와야합니다.

viewModelScope.launch(Dispatchers.IO) {
    allData = dao.getAll()
    // It's also possible to sync other data here
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.