본문 바로가기
Programming/Android

Fragment 에서 viewmodel 로부터 LiveData observe 할 때

by ciwhiz 2020. 4. 7.

https://blog.usejournal.com/observe-livedata-from-viewmodel-in-fragment-fd7d14f9f5fb를 번역/발췌함

 

Observe LiveData from ViewModel in Fragment

Google introduced Android architecture components which are basically a collection of libraries that facilitate robust design, testable…

blog.usejournal.com

문제점 : Fragment 에서 ViewModel을 사용하여 LiveData를 observing하는 경우에,
예를 들어 AA Fragment 에서 BB Fragment 로 replace 하는 경우에. BB fragment 에서 back key로

AA Fragment 로 돌아왔는데, 

LiveData observer 가 두 번 trigger 됨.(즉, 화면 상에 livedata가 뒤늦게 업데이트 되는 게 눈에 보이거나
할 수 있음)

 

아래와 같이 사용할 때 해당 현상이 발생함. 

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    final TestViewModel viewModel =          ViewModelProviders.of(getActivity()).get(TestViewModel.class);
    viewModel.getTestEntity().observe(this, testEntity -> {
         //Do something
    });
}

Debuggind을 위해 아래와 같이 코드를 넣어보면 onChanged()가 여러 번 호출되는 것을 확인할 수 있다.

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    final ProductListViewModel viewModel =
            ViewModelProviders.of(getActivity()).get(ProductListViewModel.class);
    viewModel.getProducts().observe(this, new Observer<List>() {
        @Override
        public void onChanged(List productEntities) {
            Log.d("TEST", "[onChanged]: " + hashCode());
            //Do something
        }
    });
}

만약 User가 BB fragment 로 다시 갔다고 또 Back key로 AA fragment 로 돌아오면 LiveData observer가 세번 

불림.

그래서 이에 대해 세 가지를 알고 있어야함.

1. Fragment lifecycle

2. 어떻게 LiveData Observer를 remove 할 것인가.

3. 왜 LiveData observing을 onActivityCreated() 에서 해야 하는가

1. Fragment Lifecycle

Image from https://github.com/xxv/android-lifecycle

중요한 fragment의 View의  lifecycle의 흥미로운 점.(https://developer.android.com/reference/androidx/fragment/app/Fragment#getViewLifecycleOwner())

2. 어떻게 LiveData observer를 제거할 것인가

LiveData 와 LifecycleOwner 에 대한 developer document 에 보면,

https://developer.android.com/topic/libraries/architecture/livedata#work_livedata

 

LiveData 개요  |  Android 개발자  |  Android Developers

LiveData를 사용하여 수명 주기를 인식하는 방식으로 데이터를 처리합니다.

developer.android.com

말인 즉슨, LifecycleOwener 를 사용하면 알아서 memory leak 도 없이 activity와 fragment 에서 livedata를

observer 할 수 있다는 얘기..

이에 대한 solution은 아래와 같다.

- Fragment AA가 Fragment BB에 의해 replace 되고 transaction이 backstack에 add 될 떄, 

  Fragment AA의 lifecycle의 상태는 onDestroyView 다.

- User가 Fragment BB에서 back key를 눌렀을 때, Fragment AA는 onCreateView() -> onViewCreated() -> 
  onActivityCreate() 순서를 거친다.
- Fragment AA는 결코 destroy 되지 않기 때문에, 이전 observer 역시 remove 되지 않았다. 
  결국, onActivityCreated()가 매번 call 될 때, 새로운 observer 가 등록되고 이전 observer 역시 남아있는 상태이다.
  이것이 onChanged()가 여러번 호출되는 원인이다.

- 이에 대한 한 가지 해결책은 onActivityCreated() 내부에서 LiveData를 observing하는 동안,
  LifeCycleOwner로 getViewLifeCycleOwner()를 사용하는 것이다. 

viewModel.getMainTab().observe(getViewLifecycleOwner(), new Observer() {
    @Override
    public void onChanged(@Nullable Integer integer) {
        //Do something
    }
});

3. 왜 onCreate()가 아닌 onActivityCreated()에서 observe 하는가?   

developer 문서에 따르면,

일반적으로 LiveData는 data change가 발생할 때만 active observer에게 업데이트를 delivery 한다.

이러한 동작의 예외상황이 될 수 있는 것은 observer가 inactive 상태에서 active 상태로 변경될 때,

업데이트를 수신한다는 점이다. 또한 observer가 inactive -> active 로 다시 변경되면, 마지막으로

active 상태가 된 이후 값이 변경된 경우에만 업데이트를 받는다.

 

- 만약 onCreate()에서 observe 하고, Fragment의 View가 recreate 된다면(visible -> backstack -> comes back), 

  수동으로 ViewModel을 통해 value를 업데이트해주어야만 한다.

  왜냐면, LiveData는 이미 observer에게 마지막 결과값을 전달했기때문에 다시 observer를 call하지 않기 때문이다.

결론

- onActivityCreated()안에서 LiveData를 observe 할 때, getVIewLifecycleOwner() 를 사용해라 
- 만약 onCreate()에서 LiveData를 observe 하고 싶으면 Fragment가 recreate 될 때 view와 value를

  수동으로 업데이트 해줘야한다.