- Without ViewModel
- With ViewModel
- ViewModel With SavedStateHandle
(1) Without ViewModel
Application
class SSApplication : DaggerApplication() {
override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
return DaggerAppComponent.builder()
.application(this)
.build()
}
}
AppModule
@Module
class AppModule {
@Binds
@Singletone
fun bindsApplication(app:SSApplication) : Application
}
ActivityModule
@Module
interface ActivityBindingModule {
@ActivityScope
@ContributesAndroidInjector(modules = [MainModule::class])
fun bindsMainActivity(): MainActivity
}
Component
@Singleton
@Component(
modules = [
AndroidInjectionModule::class,
AppModule::class,
ActivityBindingModule::class
]
)
interface AppComponent : AndroidInjector<SSApplication> {
@Component.Builder
interface Builder {
@BindsInstance
fun application(app: Application): Builder
fun build(): AppComponent
}
}
(2) With ViewModel (MultiBinding)
ViewModelFactory
@Singleton
class ViewModelFactory @Inject constructor(
private val mViewModels: MutableMap<Class<out ViewModel>, Provider<ViewModel>>
): ViewModelProvider.Factory{
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return mViewModels[modelClass]?.get() as T
}
}
ViewModelKey
@Target(
AnnotationTarget.FUNCTION,
AnnotationTarget.PROPERTY_GETTER,
AnnotationTarget.PROPERTY_SETTER
)
@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
@MapKey
internal annotation class ViewModelKey(val value: KClass<out ViewModel>)
ViewModelModule
@Module
abstract class ViewModelModule {
@Multibinds
abstract fun bindsViewModels(): Map<Class<out ViewModel>, @JvmSuppressWildcards ViewModel>
@Binds
@IntoMap
@ViewModelKey(MainViewModel::class)
abstract fun bindsMainViewModel(factory: MainViewModel.Factory)
: AssistedSavedStateViewModelFactory<out ViewModel>
}
MainViewModel
class MainViewModel : ViewModel() {
var count = MutableLiveData(0)
}
Activity
class MainActivity : DaggerAppCompatActivity() {
@Inject
lateinit var viewModelProvider: ViewModelProvider
private lateinit var tv: TextView
private lateinit var btn: Button
private lateinit var finish: Button
private lateinit var viewModel: MainViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewModel = viewModelProvider.get(MainViewModel::class.java)
tv = findViewById(R.id.tv)
btn = findViewById(R.id.btn)
finish = findViewById(R.id.finish)
btn.setOnClickListener {
viewModel.count.value = (viewModel.count.value ?: 0) + 1
showCount()
}
finish.setOnClickListener {
finish()
}
showCount()
}
private fun showCount() {
tv.text = "Count is ${viewModel.count.value}"
}
}
(3) ViewModel With SavedStateHandle
AssistedSavedStateViewModelFactory
interface AssistedSavedStateViewModelFactory<T : ViewModel> {
fun create(savedStateHandle: SavedStateHandle): T
}
InjectingSavedStateViewModelFactory
@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()
}
}
VIewModelModuel 변경
@AssistedModule
@Module(includes = [AssistedInject_ViewModelModule::class])
abstract class ViewModelModule {
@Multibinds
abstract fun bindsViewModels(): Map<Class<out ViewModel>, @JvmSuppressWildcards ViewModel>
@Multibinds
abstract fun bindAssistedViewModels(): Map<Class<out ViewModel>, @JvmSuppressWildcards AssistedSavedStateViewModelFactory<out ViewModel>>
@Binds
@IntoMap
@ViewModelKey(MainViewModel::class)
abstract fun bindsMainViewModel(factory: MainViewModel.Factory): AssistedSavedStateViewModelFactory<out ViewModel>
}
MainModule
@Module
class MainModule {
@Provides
@ActivityScope
fun provideViewModelProvider(
activity: MainActivity,
viewModelFactory: InjectingSavedStateViewModelFactory
): ViewModelProvider {
return ViewModelProvider(activity, viewModelFactory.create(activity))
}
}
MainViewModel
class MainViewModel @AssistedInject constructor(
@Assisted private val savedStateHandle: SavedStateHandle
) : ViewModel() {
var count = savedStateHandle.getLiveData<Int>(0)
@AssistedInject.Factory
interface Factory : AssistedSavedStateViewModelFactory<MainViewModel>
}