【Android】Single Activity에서의 sharedViewModel의 스코프에 대해 【Koin】

소개



Koin에서 ViewModel을 주입 할 때 viewModel()sharedViewModel()의 두 가지 방법이 있습니다.
내가 담당하고 있는 앱에서는 기본 Fragment-ViewModel에서 일대일 관계가 되어 있지만, ViewPager를 사용하거나 하면 ViewModel을 Fragment간에 공유하고 싶은 장면이 많아, 그럴 때 sharedViewModel() 활약해 준다.

단지, 지금까지 어쩐지 sharedViewModel() 를 사용해 왔지만 그러므로, 오지 않아 조금 늪에 빠져 버렸다.
이번은 그런 sharedViewModel() 로 공유한 ViewModel의 스코프에 대해 소개.

sharedViewModel이란?



앞서 언급했듯이 ViewModel을 Fragment간에 공유 할 수있는 ViewModel을 주입합니다.
라고, 지금까지 생각하고 있었다.

실은 이 sharedViewModel() , 인스턴스를 공유하는 스코프가 디폴트로 Activity가 되어 있는 것 같다.
안의 실장을 깜짝 들여다 보면, 이런 느낌이 되고 있다.
/**
 * Lazy getByClass a viewModel instance shared with Activity
 *
 * @param qualifier - Koin BeanDefinition qualifier (if have several ViewModel beanDefinition of the same type)
 * @param from - ViewModelStoreOwner that will store the viewModel instance. Examples: "parentFragment", "activity". Default: "activity"
 * @param parameters - parameters to pass to the BeanDefinition
 */
inline fun <reified T : ViewModel> Fragment.sharedViewModel(
        qualifier: Qualifier? = null,
        noinline from: ViewModelStoreOwnerDefinition = { activity as ViewModelStoreOwner },
        noinline parameters: ParametersDefinition? = null
): Lazy<T> = kotlin.lazy { getSharedViewModel<T>(qualifier, from, parameters) }

보고 싶은 것은, 제2 인수의 from의 곳.
코멘트에서 ViewModelStoreOwner that will store the viewModel instance. Examples: "parentFragment", "activity". Default: "activity" 와 같이, 디폴트에서는 ViewModel 의 인스턴스를 보존하고 있는 장소가 activity 가 되어 있다.

그 때문에, 「ViewModel를 Fragment간에 공유하고 싶을 때에 사용한다」라고 하는 인식은 잘못되어, 「Activity내에서 공유할 수 있는 ViewModel를 인젝트한다」라고 하는 인식이 옳다고 한다.

범위를 올바르게 인식하지 못한 문제


sharedViewModel() 의 스코프를 제대로 인식할 수 없었던 것을 알았지만, 이것이 원인인 버그가 발생하고 있었다.

그것이 아래 이미지 패턴입니다.
이런 식으로 FragmentAFragmentB 가 타고 있는 화면을 만들고 싶었고, 두 개의 Fragment에서 ViewModel을 공유하기 위해 sharedViewModel() 를 사용하고 있었다.



그래서이 화면은 실제로 아래 그림과 같이 같은 화면으로 전이하는 도선을 가지고있었습니다.



이 때 ViewModel은 화면 천이시에 새롭게 생성된다고 생각하고 있었다.

실은 이 ViewModel, sharedViewModel() 를 사용하고 있기 때문에, 화면 천이를 해도 같은 인스턴스의 ViewModel이 사용되고 있다.
자신의 프로젝트는 SingleActivity로 만들어져 있기 때문에, 기본적으로 앱이 기동하고 있는 동안 계속 ViewModel의 인스턴스가 파기되지 않는 상태가 되어 있었다.
이대로는 이전 데이터가 남아 있거나 쓸데없이 인스턴스가 남아있습니다.

해결 방법



해결책은 그리 어렵지 않다.
ViewModel을 유지해 두는 스코프를, 각 Fragment로 해 주면 좋을 뿐.

아까 본 sharedViewModel() 의 제 2 인수의 디폴트 인수가 { activity as ViewModelStoreOwner } 로 되어 있었으므로, 이 녀석을 바꾸어 준다.
private val viewModel: ViewModel by sharedViewModel()

이 녀석
private val viewModel: ViewModel by sharedViewModel(from = { requireParentFragment() })

이렇게!

요약



SingleActivity로 앱을 만들고 있다면, 스코프를 Activity로 하고 싶은 장면은 별로 없다고 생각하기 때문에, 어쩌면 기본 parentFragment를 지정해 주는 것이 좋은 것일지도 모른다.
Dagger도 그렇지만, DI는 설정이 어렵고 어쩐지 사용하기 쉽지만, 역시 이런 것 제대로 문서 읽지 않으면 안 된다.

끝.

좋은 웹페이지 즐겨찾기