binding.lifecycleOwner = this 사용한 것이 원인.
원래 activity 단에서는 viewModel을 초기화한 뒤에, 현재 activity를 binding 객체의 lifecycleOwner로 지정함으로서 liveData 객체의 범위를 정의해주어 데이터의 변화를 data binding한 ui에 자동적으로 고지하게 해 사용했었다.하지만 fragment의 경우는 다르단걸 알지 못했다. <<< fragment의 경우는 lifecycle이 2가지가 있고, 이를 이용해야한다.
따라서 liveData를 이용해 dataBinding을 사용해 view를 update하려할려면 fragment의 viewlifecycle을 사용하는 것이 더 적절하다.
즉 binding.lifecycleOwner = this가 아니라
binding.lifecycleOwner = this.viewLifeCycleOwner를 사용해야한다.
여기까지는 검색을 통해 이유를 알았지만, 이와 관련하여 정확히 알지 못 했다는 생각이 들어, 이를 알고 넘어가기 위해 아래 좀 더 자세히 글을 작성해보려함.
참고 사이트
앱을 개발할 경우, 생명주기 관리는 여러므로 문제상황이 많은 만큼 고려해야할 것도 많고, 공부도 해야하는 사항이다. _ ( 화면이 회전한다거나 전화가 오는 등의 이벤트 발생 시에도 앱을 계속 잘 사용할 수 있게 하는 것은 중요하기에...)
이때, activity와는 다른, 자체의 lifecycle을 가진 fragment를 혼합하여 사용할 경우 문제는 좀 더 복잡해지고 관리해줘야하는 것들 또한 많아진다. 만약 관리를 해주기 않는 다면, onCreate(), onResume() 등에 코드가 많아져 유지관리가 어려워지는 문제가 발생하고, 필요이상으로 생명주기가 오래 유지되거나 다른 예외상황등이 발생 할 수 있음.
특히 fragment의 경우, attach된 액티비티의 생명주기에 영향을 받을 뿐더러 이 경우 여러개의 프래그먼트를 하나의 액티비티에서 사용하거나, 반대로 하나의 프래그먼트를 여러 액티비티에서 재사용하는 경우도 있어 좀 더 주의깊게 관리해줘야한다.
아래는 activiy와 fragment의 lifecycle에 대해 비교한 사진이다.
이런 복잡한 생명주기 관리를 보다 편하게 하기 위하여, ACC에서는 lifecycle이라는 component를 제공, 이는 UI 구성요소의 생명주기의 모니터링을 도와주며 activity와 fragment를 더 가볍게 유지할 수 있게 해준다.
lifecycle은 main enum 클래스 2개와 interface 2개 로 구성된다.
main enum 클래스 : EVENT, STATES를 가지며 이 둘을 가지고 연결된 구성요소의 수명주기를 추적한다.
interface : lifecycleOwner와 lifecycleObserver로 구성된다.
즉 liveData + viewModel + dataBinding과 함께 이용 시 두각을 드러냄.
관련 사이트 : https://developer.android.com/guide/fragments/lifecycle?hl=ko#fragment_created_and_view_initialized
프래그먼트의 view lifecycle은 fragment가 유효한 view instance를 제공할 때만 생성된다. 사용 할 수 있는 단계는 그 뒤 onCreateView()을 오버라이드한 뒤에 프래그먼트 뷰를 inflate할 때이다.
그 다음 getViewLifecycleOwnerLiveData()가 fragment view와 함께 새로 initalized된 lifecycleOwner를 업데이트하고, onViewCreated()를 호출하는 순서이다.
때문에 onViewCreated()는 뷰 상태를 초기화하거나, livedata를 옵저빙하거나, recyclerView나 ViewPager2의 어뎁터를 셋팅하기 적절한 위치이다.
(<< 하지만 viewLifecycle이 생성된 후 onCreateView가 호출되는 만큼, onCreateView에서도 viewLifecycleOwner를 사용할 수 있음.
공식문서에서는 onViewCreated()를 권장할 뿐)
<다시 문제 상황으로 돌아와, 위의 내용을 참조해 다시 설명할 경우>
1. fragment에서 뷰모델의 정의된 livedata를 databinding하기 위해서 binding.lifecycleOwner = this 해 준 경우
2. viewModel.livedata.observe(this, Observer { } ) 요 상황에서 프래그먼트 내 화면전환 할 경우
옵저빙을 중복으로 한다거나, databinding에 값이 제대로 들어가지 않는 현상 발생.
원인 : onResume() 되었을 경우 activity는 onDestroy되는 것에 비해, fragment는 onDestroy() 되지 않고 onCreateView()를 호출.
즉 fragment의 생명주기 참조 시 아래 그림 처럼, DESTROYED되지 않고 onCreateView()만 여러번 호출되게 됨.
하지만 위 그림을 보면 fragment view의 경우는 onResume()시onDestroyView()를 하면서 DESTROYED 상태가 됨.
해결방안 :
1. binding.lifecycleOwner = this.viewLifecycleOwner
2. viewModel.livedata.observe(viewLifecycleOwner, Observer { } )
을 사용할 경우 viewLifecycle은 DESTROYED로 폐기되기 때문에, 중복이나 다른 문제 없이 잘 돌아가게 된다.
사실 이 문제는 내가 마주한 1번 상황(binding.lifecycleOwner)과 달리 2번 상황처럼 data binding 하지 않고 observer를 구현할 때도 나타나는 문제이다. 하지만 나는 dataBinding을 해보면서 이를 마주했는데 이유는 처음에 옵저빙 관련해서 공부할 때, 코드를 먼저 따라쳐보면서 해서 observe()할 때 fragment에서 제대로 viewLifecycleOwner라고 사용을 했었다.
제대로 알아보지 않고 적당히 프래그먼트에서는 옵저버를 이리 쓰는구나하고 넘겼던 폐해를 이리 직면해, 많이 반성하면서 공부하는 시간이었다.
좋은 글 감사합니다