본문 바로가기

Android

[Android] View에서 by viewModels() 사용하기

❗주의

이 Extension은 Hilt DI 라이브러리 사용과 Activity 또는 Fragment에 @AndroidEntryPoint 어노테이션이 선언되어 있는 경우 유용하게 사용 가능합니다.

import android.app.Activity
import android.content.Context
import android.content.ContextWrapper
import android.view.View
import androidx.activity.ComponentActivity
import androidx.annotation.MainThread
import androidx.lifecycle.HasDefaultViewModelProviderFactory
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelLazy
import androidx.lifecycle.ViewModelProvider.Factory
import androidx.lifecycle.ViewModelStoreOwner
import androidx.lifecycle.findViewTreeViewModelStoreOwner
import androidx.lifecycle.viewmodel.CreationExtras

fun Context.findActivity(): Activity {
    var context = this
    while (context is ContextWrapper && context !is Activity) {
        context = context.baseContext
    }
    return context as Activity
}

@MainThread
inline fun <reified VM : ViewModel> View.viewModels(
    noinline ownerProducer: () -> ViewModelStoreOwner = { findViewTreeViewModelStoreOwner()!! },
    noinline extrasProducer: (() -> CreationExtras)? = null,
    noinline factoryProducer: (() -> Factory)? = null
): Lazy<VM> {
    val owner by lazy(LazyThreadSafetyMode.NONE) { ownerProducer() }
    return ViewModelLazy(
        VM::class,
        { owner.viewModelStore },
        factoryProducer ?: {
            (owner as HasDefaultViewModelProviderFactory).defaultViewModelProviderFactory
        },
        {
            extrasProducer?.invoke()
                ?: (owner as? HasDefaultViewModelProviderFactory)?.defaultViewModelCreationExtras
                ?: CreationExtras.Empty
        },
    )
}

@MainThread
inline fun <reified VM : ViewModel> View.activityViewModels(
    noinline extrasProducer: (() -> CreationExtras)? = null,
    noinline factoryProducer: (() -> Factory)? = null
): Lazy<VM> {
    val owner by lazy(LazyThreadSafetyMode.NONE) { context.findActivity() as ComponentActivity }
    return ViewModelLazy(
        VM::class,
        { owner.viewModelStore },
        factoryProducer ?: {
            (owner as HasDefaultViewModelProviderFactory).defaultViewModelProviderFactory
        },
        {
            extrasProducer?.invoke()
                ?: (owner as? HasDefaultViewModelProviderFactory)?.defaultViewModelCreationExtras
                ?: CreationExtras.Empty
        },
    )
}