https://blog.usejournal.com/observe-livedata-from-viewmodel-in-fragment-fd7d14f9f5fb를 번역/발췌함
문제점 : 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
중요한 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
말인 즉슨, 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를
수동으로 업데이트 해줘야한다.