[Android] Fragment view lifecycle 관련 + liveData

이유정·2022년 1월 29일
7
post-thumbnail

문제 상황 :

  • viewModel에 LiveData를 만들어두고, xml 상에서 dataBinding을 할 때, fragment에서 ui가 업데이트가 안되거나, 예외상황이 발생하는 등의 문제가 발생.

원인 & 해결방법:

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

lifecycle은 main enum 클래스 2개와 interface 2개 로 구성된다.

main enum 클래스 : EVENT, STATES를 가지며 이 둘을 가지고 연결된 구성요소의 수명주기를 추적한다.

  • EVENT는 전달된 수명 주기 이벤트이며, activity나 fragment의 콜백 이벤트에 맵핑된다. (onCreate() 와 같은,,,)
  • STATUS는 lifecycle 객체가 추적한 구성요소의 현 상태를 뜻한다.

interface : lifecycleOwner와 lifecycleObserver로 구성된다.

  • lifecycleOwner는 getLifecycle()을 통해 클래스에 lifecycle이 있음을 나타내는 단일 메서드 인터페이스로서 fragment나 activiy의 lifecycle의 소유권을 추출해 가지고 있으며 이를 lifecycle에 제공한다.
  • lifecycleObserver는 lifecycle로 부터 상태변화에 대한 이벤트를 감지하고 알려줍니다.

+) lifeCycle는 아래의 경우 사용을 권장한다.

  • ui controller가 data를 수집하지 않고 viewModel에 위임한 상태
  • liveData를 observing하여 view를 update할 때,
  • view와 ui component의 관계를 dataBinding 할 때

즉 liveData + viewModel + dataBinding과 함께 이용 시 두각을 드러냄.

Fragment View lifecycle.

관련 사이트 : 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라고 사용을 했었다.
제대로 알아보지 않고 적당히 프래그먼트에서는 옵저버를 이리 쓰는구나하고 넘겼던 폐해를 이리 직면해, 많이 반성하면서 공부하는 시간이었다.

profile
개인 공부 블로그

2개의 댓글

comment-user-thumbnail
2022년 12월 27일

좋은 글 감사합니다

답글 달기
comment-user-thumbnail
2024년 3월 13일

문제 상황과 이론, 마지막으로 문제 원인에다 해결까지 깔끔하게 해주셔서 이해가 잘 되네요. 감사합니다!

답글 달기