https://developer.android.com/kotlin/ktx#fragment 이쪽 코드를 보다가 생성자가 있는 ViewModel의경우 Factory를 만들어야하는 것이 좀 거슬려서 factoryPorducer를 viewModelProducer로 바꿔서 수정해 봤다.
package com.mdiwebma.base.kotlin_ext
import androidx.activity.ComponentActivity
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelStore
class ViewModelLazy<ViewModelType : ViewModel>(
private val viewModelClass: Class<ViewModelType>,
private val storeProducer: () -> ViewModelStore,
private val viewModelProducer: (() -> ViewModelType)? = null
) : Lazy<ViewModelType> {
private var cached: ViewModelType? = null
override val value: ViewModelType
get() {
val viewModel = cached
return if (viewModel == null) {
val factory = if (viewModelProducer != null) {
object : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return viewModelProducer.invoke() as T
}
}
} else {
ViewModelProvider.NewInstanceFactory()
}
val store = storeProducer()
ViewModelProvider(store, factory).get(viewModelClass).also {
cached = it
}
} else {
viewModel
}
}
override fun isInitialized() = cached != null
}
inline fun <reified ViewModelType : ViewModel> ComponentActivity.viewModel(
noinline viewModelProducer: (() -> ViewModelType)? = null
): Lazy<ViewModelType> = ViewModelLazy(
ViewModelType::class.java,
{ viewModelStore },
viewModelProducer
)
inline fun <reified ViewModelType : ViewModel> Fragment.viewModel(
noinline viewModelProducer: (() -> ViewModelType)? = null
): Lazy<ViewModelType> = ViewModelLazy(
ViewModelType::class.java,
{ viewModelStore },
viewModelProducer
)
inline fun <reified ViewModelType : ViewModel> Fragment.activityViewModel(
noinline viewModelProducer: (() -> ViewModelType)? = null
): Lazy<ViewModelType> = ViewModelLazy(
ViewModelType::class.java,
{ requireActivity().viewModelStore },
viewModelProducer
)
사용할 때는
// 생성자가 있는 viewModel
private val vm: TestViewModel by viewModel { TestViewModel(intent.toString()) }
// 생성자가 없는 ViewModel
private val vm: TestViewModel by viewModel()
// Activity의 viewModel
private val vm: TestViewModel by activityViewModel()
조금 개선.. lazy 말고 바로 getViewModel 로 즉시 생성할 수도 있게
class InternalViewModelFactory<ViewModelType : ViewModel>(
private val viewModelProducer: (() -> ViewModelType)
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return viewModelProducer() as T
}
}
inline fun <reified ViewModelType : ViewModel> ComponentActivity.viewModel(
noinline viewModelProducer: (() -> ViewModelType)? = null
) = lazy {
getViewModel(viewModelProducer)
}
inline fun <reified ViewModelType : ViewModel> ComponentActivity.getViewModel(
noinline viewModelProducer: (() -> ViewModelType)? = null
) = ViewModelProvider(
this,
if (viewModelProducer != null) InternalViewModelFactory(viewModelProducer)
else ViewModelProvider.NewInstanceFactory()
).get(ViewModelType::class.java)
inline fun <reified ViewModelType : ViewModel> Fragment.viewModel(
noinline viewModelProducer: (() -> ViewModelType)? = null
) = lazy {
getViewModel(viewModelProducer)
}
inline fun <reified ViewModelType : ViewModel> Fragment.getViewModel(
noinline viewModelProducer: (() -> ViewModelType)? = null
) = ViewModelProvider(
this,
if (viewModelProducer != null) InternalViewModelFactory(viewModelProducer)
else ViewModelProvider.NewInstanceFactory()
).get(ViewModelType::class.java)
inline fun <reified ViewModelType : ViewModel> Fragment.activityViewModel(
noinline viewModelProducer: (() -> ViewModelType)? = null
) = lazy {
getActivityViewModel(viewModelProducer)
}
inline fun <reified ViewModelType : ViewModel> Fragment.getActivityViewModel(
noinline viewModelProducer: (() -> ViewModelType)? = null
) = ViewModelProvider(
requireActivity(),
if (viewModelProducer != null) InternalViewModelFactory(viewModelProducer)
else ViewModelProvider.NewInstanceFactory()
).get(ViewModelType::class.java)