Android/note

Coroutine + LiveData 를 통해 데이터 전달받기 그런데 이전 데이터도 전달되는 ..

김 안개 2024. 1. 26. 16:25

문제 상황

첫번째, dp 알고리즘 클릭 >>

 

두번째, 그래프 이론 알고리즘을 클릭하면

 

이전에 선택한 dp 관련 문제들이 로그에 찍히고

 

 

그 이후에 그래프 이론 알고리즘 관련 문제들이 로그에 찍히는 것을 알 수 있다.

다른 알고리즘을 클릭하고 문제를 보고를 반복하다보면 exception 이 발생하는 문제를 마주하게 되었다.

 

문제 해결 과정

1️⃣ ViewModel 에서 LiveData 에 값을 세팅하는 과정을 확인

 

네트워크 통신을 통해 가져온 데이터를 가공해서 _problems.value 를 통해 값을 세팅해주었다.

하지만 그런데도 이전 값과 + 최신 값이 로그에 똑같이 찍혔다.

 

 

무엇을 확인해봐야 하는지 감이 잡히지 않아서 챗GPT 에게 물어봤고, 하나씩 확인해보기로 했다.

 

2️⃣ Observer 등록 여부

problems 을 관찰하기 위해 등록되어있는 observer 가 중복으로 등록되어 있지 않는지 확인하였다.

사용하는 곳은 딱 한곳!

문제를 보여주는 Fragment 에만 observer 가 등록되어있다.

3️⃣ ViewModel 등록 여부

 

문득 .. ViewModel 의 라이프사이클이 잘못 등록되어있나 ..? 정말 문득 .. 갑자기 생각이 들었다.

 

습관적으로 requireActivity() 를 넣는 안좋은 습관이 있는데, 문득 .. 이게 requireActivity() 가 들어가는 게 아니라 다른 것이 들어가야하는건가 ..? 하는 생각이 들었다 ..

 

그래서 ViewModelProvider 의 내부를 살펴보았는데

 

// ViewModelProvider.kt

public constructor(owner: ViewModelStoreOwner, factory: Factory) : this(
    owner.viewModelStore,
    factory,
    defaultCreationExtras(owner)
)

 

requireActivity() 가 들어가는 곳은 owner: ViewModelStoreOwner 의 위치이다.

 

그럼 ViewModelStoreOwner 는 무엇인가?

 

// ViewModelStoreOwner.kt

interface ViewModelStoreOwner {

    /**
     * The owned [ViewModelStore]
     */
    val viewModelStore: ViewModelStore
}

 

ViewModelStroeOwner 는 인터페이스였고, 내부적으로 ViewModelStore 변수를 가지고 있었다.

A scope that owns ViewModelStore.
A responsibility of an implementation of this interface is to retain owned ViewModelStore during the configuration changes and call ViewModelStore.clear, when this scope is going to be destroyed.

 

대강 살펴보면 구성 변경이 되거나 ViewModelStore.clear 가 호출되는 동안에 가지고 있는 ViewModelStore 를 유지하기 위한 책임을 가진 인터페이스이다.

그렇다면 나는 Fragment 에서 사용을 하고 있고, ProblemViewModel 은 ProblemFragment 외에는 사용이 되지 않기 때문에 VIewModelStoreOwner 는 ProblemFragment 가 되어야 한다는 것을 알았다.

requireActivity() 를 넣게되면 ProlemFragment 와 연관이 있는 FragmentActivity() 가 ViewModelStoreOwner 가 되는 것인데, 나의 경우에는 MainActivity 였다. ==> 잘못 연결되어있음을 인지

 

그런데 ProblemFragment 가 ViewModelStoreOwner 가 되려면 ViewModelStoreOwner 를 구현하고 있어야 하는데, 이 부분을 찾아보았다.

 

public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener, LifecycleOwner,
        ViewModelStoreOwner, HasDefaultViewModelProviderFactory, SavedStateRegistryOwner,
        ActivityResultCaller { ... }

 

다행히 .. 프레그먼트는 ViewModelStoreOwner 를 구현하고 있기때문에 requireActivity() 대신 this 를 사용해주면 되었다!

 

 

this 로 변경해주고 로그를 다시 찍어보았더니 이전 데이터는 보여지지 않고 새로 전달받은 데이터만 보여졌다!

문제 해결완 ...