[Android] Fragment에서 viewModel 접근을 onAttach() 이후에 해야하는 이유

2021. 7. 17. 21:51Android

반응형

TL;DR: 

Fragment 에서 아래와 같이 viewModel 생성했을 때, onAttach() 메서드가 호출된 이후에  viewModel 에 접근해야 한다. 

private val viewModel by viewModels<YourViewModel>()

올바른 접근 (⭕️)

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        arguments?.apply {
            viewModel.initAddTask(getInt(ARG_TASK_ID).toLong())
        }
 }

올바르지 않은 접근 (❌)

    override fun onAttach(context: Context) {
        super.onAttach(context)

        arguments?.apply {
            viewModel.initAddTask(getInt(ARG_TASK_ID).toLong())
        }
    }

 

 

🚧 바로 위의 예제와 같이, onAttach() 내에서 viewModel 에 접근하면, 아래와 같은 오류를 마주칠 것이다. 

java.lang.IllegalStateException: You can consumeRestoredStateForKey only after super.onCreate of corresponding component

 

 

오류가 발생하는 이유 

Fragment.viewModels() 의 내부 구현체를 보자. 

주석을 살펴보면,

This property can be accessed only after this Fragment is attached i.e., after* [Fragment.onAttach()]

이 프로퍼티(viewModel) 은 fragment 가 attached 된 이후에 접근 가능하다 라고 쓰여있다. 

어떻게 보면 당연하다고 생각되는 것이, viewModels() 로 생성 시 viewModel 의 default scope(기본 범위)가 해당 fragment 이기 때문에.

 *
 * This property can be accessed only after this Fragment is attached i.e., after
 * [Fragment.onAttach()], and access prior to that will result in IllegalArgumentException.
 */
@MainThread
inline fun <reified VM : ViewModel> Fragment.viewModels(
    noinline ownerProducer: () -> ViewModelStoreOwner = { this },
    noinline factoryProducer: (() -> Factory)? = null
) = createViewModelLazy(VM::class, { ownerProducer().viewModelStore }, factoryProducer)

 

단, activityViewModels() 로 생성 후 접근하면 오류가 발생하지 않는다. 

Fragment 에서 아래와 같이 activityViewModels 로 생성하면 onAttach() 에서 viewModel 에 접근해도 위와 같은 오류는 발생하지 않았다. 

private val viewModel by activityViewModels<YourViewModel>()

 

실제로 activityViewModels 의 내부 구현체를 보면, viewModels 와 동일하게 onAttach() 후에 접근해야 한다고 쓰여 있으나, 

 * This property can be accessed only after this Fragment is attached i.e., after
 * [Fragment.onAttach()], and access prior to that will result in IllegalArgumentException.
 */
@MainThread
inline fun <reified VM : ViewModel> Fragment.activityViewModels(
    noinline factoryProducer: (() -> Factory)? = null
) = createViewModelLazy(VM::class, { requireActivity().viewModelStore },
    factoryProducer ?: { requireActivity().defaultViewModelProviderFactory })

실제로 테스트했을 때 정상적으로 동작한 이유는,

activityViewModels 로 viewModel 을 생성한다는 것은 fragment 의 parent activity scope의 viewModel 을 사용하는 것으로, parent activity 는 이미 생성되어 있는 상태기 때문에 별다른 exception 없이 동작한 것으로 생각된다. 

 

 

 

 

반응형