Simple&Natural

ViewPager2 사용 시 ViewModel이 onCleared 된 후 다시 초기화되지 않는 이슈 관련 회고 본문

안드로이드(Android)/이슈 및 해결

ViewPager2 사용 시 ViewModel이 onCleared 된 후 다시 초기화되지 않는 이슈 관련 회고

Essense 2022. 12. 22. 14:48
728x90

 

일반적인 MVVM 패턴에서 ViewModel을 편리하게 사용할 수 있도록

Android는 자체적으로 ViewModel 관련 컴포넌트들을 제공하고 있다.

이때, ViewModel은 생명주기를 가지는데 이는 처음 생성할 때 지정했던 ViewModelStoreOwner에 따라 달라진다.

다음 코드를 보자.

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    viewModel = ViewModelProvider(
        this,
        MyCommunityListViewModel.Factory(
					requireAppContainer().communityRepository,
					requireAppContainer().settingInfo
        )
    ).get(MyCommunityListViewModel::class.java)
}

특정 Fragment에서 ViewModel을 생성하는 부분이다.

이때 owner를 해당 Fragment 로 지정했으므로 이 ViewModel의 생성과 파괴는 해당 Fragment에 의존한다.

Activity → Fragment → ViewPager → Fragments

구조로 되어 있는 경우

ViewPager 하위의 Fragment에서 위 코드를 사용하면 ViewPager를 스크롤함에 따라

Fragment 와 ViewModel의 생성과 파괴가 반복된다.

만약 해당 코드를 다음과 같이 변경하면 어떨까?

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    viewModel = ViewModelProvider(
        requireParentFragment().viewModelStore,
        MyCommunityListViewModel.Factory(
					requireAppContainer().communityRepository,
					requireAppContainer().settingInfo
        )
    ).get(MyCommunityListViewModel::class.java)
}

굵게 표시된 부분을 보면

this 에서 requireParentFragment().viewModelStore 로 바뀌었다.

이는 ViewPager를 가지고 있는 상위 Fragment로 ViewModel의 생성과 파괴 주기가 지정된 것을 뜻한다.

즉, 스크롤에 상관없이 ViewPager가 유지되는 한 ViewModel의 데이터는 지속적으로 남아있음을 의미한다.

만약 스크롤에 관계없이 탭 내의 데이터를 지속해서 유지하고 싶은 경우 위처럼 owner를 지정하면 되지만

이 경우엔 view가 파괴되었음에도 해당 view의 viewModel이 지속적으로 남아있는 것이 부자연스럽다.

따라서 상위 fragment의 viewModel을 생성하고 그곳에서 data를 저장하는 것이 바람직할 것이다.

private val viewModel: MyCommunityListViewModel by viewModels{
		MyCommunityListViewModel.Factory(
				requireAppContainer().communityRepository,
				requireAppContainer().settingInfo
    )
}

한 가지 더 유념해야 할 부분은

위처럼 property delegate을 이용해 ViewModel을 생성한 경우 owner는 해당 fragment로 지정되나

Fragment는 생성-파괴되지만 ViewModel은 첫 번째 onCleared 이후 다시 생성-초기화되지 않는 현상이 발생한다.

이에 대한 자세한 분석은 추가로 필요하지만, Fragment의 onDestroy 호출 시 ViewModelStoreOwner 내부에서 
ViewModel 관련 cache가 그대로 남아있는 듯 보인다.

이를 방지하기 위해 property delegate 대신 맨 위의 방식처럼 Fragment의 onCreate에 viewModel을 다시 생성하는 코드를 삽입해주어 해결하였다.

728x90