SavedStateHandle을 다루는 ViewModel을 위한 Dagger 설정하기

SavedStateHandle을 다루는 ViewModel을 위한 Dagger 설정하기

build.gradle에 의존성 설정하기

//SavedState
implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.2.0"
//Dagger2
implementation 'com.google.dagger:dagger:2.27'
implementation 'com.google.dagger:dagger-android:2.27'
implementation 'com.google.dagger:dagger-android-support:2.27'
kapt 'com.google.dagger:dagger-compiler:2.27'
kapt 'com.google.dagger:dagger-android-processor:2.27'
//AssistedInject
compileOnly "com.squareup.inject:assisted-inject-annotations-dagger2:0.5.2"
kapt "com.squareup.inject:assisted-inject-processor-dagger2:0.5.2"

AssitedInject를 위한 Base Factory 만들기

/**
* SavedStateHandle을 포함하는 ViewModel을 생성하기 위한 범용적인 Factory
* 이 Factory는 하나의 InjectingSavedStateViewModelFactory에 모든 ViewModel을 가질 수 있도록 한다.
*/
interface AssistedSavedStateViewModelFactory<T : ViewModel> {
fun create(savedStateHandle: SavedStateHandle): T
}

ViewModel에 AssistedInject를 위한 코드 추가하기

class MainViewModel @AssistedInject constructor(
@Assisted private val savedStateHandle: SavedStateHandle
) : ViewModel() {
...
@AssistedInject.Factory
interface Factory : AssistedSavedStateViewModelFactory<MainViewModel>
}

ViewModel들을 멀티바인딩으로 관리 하기

@AssistedModule
@Module(includes = [AssistedInject_ViewModelModule::class])
abstract class ViewModelModule{
//일반 뷰모델들의 멀티 바인딩
@Multibinds
abstract fun bindsViewModels(): Map<Class<out ViewModel>, @JvmSuppressWildcards ViewModel>
//AssistedInject로 관리하는 ViewModel Factory 멀티바인딩
@Multibinds
abstract fun bindsAssistedViewModels(): Map<Class<out ViewModel>, @JvmSuppressWildcards AssistedSavedStateViewModelFactory<out ViewModel>>
@Binds
@IntoMap
@ViewModelKey(MainViewModel::class)
abstract fun bindsMainViewModel(factory: MainViewModel.Factory): AssistedSavedStateViewModelFactory<out ViewModel>
}

AbstractSavedStateViewModelFactory 만들기

/**
* ViewModel들을 인스턴스화 하기 위해 이 클래스를 사용할 수 있다.
* Fragment/Activity 에서 이 Factory를 주입받아 ViewModel을 생성하는데 사용할 수 있다.
*/
@Singleton
class InjectingSavedStateViewModelFactory
@Inject constructor(
private val assistedFactories: Map<Class<out ViewModel>, @JvmSuppressWildcards AssistedSavedStateViewModelFactory<out ViewModel>>,
private val viewModelProviders: Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>
) {
/**
* @AssistedInject 또는 @Inject로 어노테이션이 달린 ViewModel 인스턴스를 작성하고 필요한 종속성을 전달한다.
*/
fun create(owner: SavedStateRegistryOwner, defaultArgs: Bundle? = null) =
object : AbstractSavedStateViewModelFactory(owner, defaultArgs) {
override fun <T : ViewModel?> create(
key: String,
modelClass: Class<T>,
handle: SavedStateHandle
): T {
val viewModel =
createAssistedInjectViewModel(modelClass, handle)
?: createInjectViewModel(modelClass)
?: throw IllegalArgumentException("Unknown model class $modelClass")
try {
@Suppress("UNCHECKED_CAST")
return viewModel as T
} catch (e: Exception) {
throw RuntimeException(e)
}
}
}
/**
* @AssistedInject 생성자와 해당 Factory를 기반으로 ViewModel을 생성한다.
*/
private fun <T : ViewModel?> createAssistedInjectViewModel(
modelClass: Class<T>,
handle: SavedStateHandle
): ViewModel? {
val creator = assistedFactories[modelClass]
?: assistedFactories.asIterable()
.firstOrNull { modelClass.isAssignableFrom(it.key) }?.value
?: return null
return creator.create(handle)
}
/**
* 생성자에 @Inject가 있는 일반적인 Dagger 기반의 ViewModel을 생성한다.
*/
private fun <T : ViewModel?> createInjectViewModel(
modelClass: Class<T>
): ViewModel? {
val creator = viewModelProviders[modelClass]
?: viewModelProviders.asIterable()
.firstOrNull { modelClass.isAssignableFrom(it.key) }?.value
?: return null
return creator.get()
}
}

액티비티에서 ViewModel 만들기

@Module
class MainModule{
@Provides
@ActivityScope
fun provideViewModelProvider(activity:MainActivity, viewModelFactory:InjectingSavedStateViewModelFactory):ViewModelProvider{
return ViewModelProvider(activity, viewModelFactory.create(activity))
}
}class MainActivity : DaggerAppCompatActivity() { @Inject
lateinit var viewModelProvider: ViewModelProvider
...
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewModel = viewModelProvider.get(MainViewModel::class.java)
...
}
...
}

Conclusion

A passionate developer who’s curious about Android

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store