使用 dagger-android 注入 ViewModel
2019-10-31 本文已影响0人
追风筝的boy
- 以下内容均为The Google I/O 2019 Android App中学习所得,你可以直接跳转到该链接进行学习
获取 ViewModel
实例的一般做法
- 首先会创建一个
MainViewModel
类,然后在类中定义一个继承自ViewModelProvider.Factory
接口的类,实现create
接口,直接通过MainViewModel
的构造方法创建了一个实例 - 然后在
Activity
或者Fragment
中, 通过ViewModelProviders.of(this, MainViewModel.Factory()).get(MainViewModel::class.java)
来获取viewModel 的实例对象
class MainViewModel : ViewModel() {
// 这里的代码,在所有的 viewModel 中都需要在写一遍,就是所谓的`Boilerplate code`
class Factory : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
@Suppress("UNCHECKED_CAST")
return MainViewModel() as T
}
}
}
class MainActivity : AppCompatActivity() {
private lateinit var viewModel: MainViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProviders.of(this, MainViewModel.Factory()).get(MainViewModel::class.java)
setContentView(R.layout.activity_main)
}
}
-
ViewModel.Factory
类的实现都是相同的 - 如果项目十分庞大的话,必然会产生巨量的样板代码(
Boilerplate code
),这给我们的维护会造成困难
使用 dagger-android 来减少 boilerplate
- 首先可以先看一下使用
Dagger
如何减少样板代码的实现 - 可以先从具体需要注入
ViewModel
的MainActivity
地方来看- 仅仅注入了一个
ViewModelProvider.Factory
接口的一个实例,然后就可以通过相应的方法来获取viewModel
的实例对象 - 在
MainViewModel
中去掉了原来的Factory
样板代码
- 仅仅注入了一个
class MainActivity : DaggerAppCompatActivity() {
@Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
private lateinit var viewModel: MainViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = viewModelProvider(viewModelFactory)
setContentView(R.layout.activity_main)
}
}
inline fun <reified T: ViewModel> AppCompatActivity.viewModelProvider(provider: ViewModelProvider.Factory): T {
return ViewModelProviders.of(this, provider).get(T::class.java)
}
class MainViewModel @Inject constructor(): ViewModel()
-
ViewModelProvider.Factory
接口的依赖是由下面定义的Module
来提供 - 然后
Dagger
会去寻找AppViewModelFactory
的实例来作为依赖的提供者,而AppViewModelFactory
的实例会通过其构造方法来创建 - 至此
ViewModelProvider.Factory
这个接口的注入已经完善 - 下面会详细的描述
AppViewModelFactory
实例创建时,其构造方法中所需依赖获取的具体流程
@Module
@Suppress("UNUSED")
abstract class ViewModelModule {
@Binds
internal abstract fun bindViewModelFactory(factory: AppViewModelFactory): ViewModelProvider.Factory
}
- 首先来看一下
ViewModelProvider.Factory
实现类
class AppViewModelFactory @Inject constructor(
private val creators: Map<Class<out ViewModel>,@JvmSuppressWildcards Provider<ViewModel>>
) : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
val find = creators.entries.find { modelClass.isAssignableFrom(it.key) }
val creator = find?.value ?: throw IllegalArgumentException("unknown modelClass class $modelClass")
return try {
@Suppress("UNCHECKED_CAST")
creator.get() as T
} catch (e: Exception) {
throw RuntimeException(e)
}
}
}
- 该类需要一个
Map<Class<out ViewModel, Provider<ViewModel>>
类型的实例 - 该实例会通过
MainActivityModule
中通过@IntoMap
标注的方法来提供- 这里涉及到multibindings来提供,不太懂的同学可以先去学习一下
- 包含该
Module
的Component
会提供以下两种Map类型的集合以供使用Map<Class, ViewModel>
Map<Class, Provider<ViewModel>>
-
Dagger
会通过@IntoMap
创建的Map<Class, Provider<ViewModel>>
类型的实例来创建AppViewModelFactory
实例,来作为ViewModelProvider.Factory
接口的依赖进行注入
@Module
abstract class MainActivityModule {
@Binds
@IntoMap
@ViewModelKey(MainViewModel::class)
abstract fun viewModel(viewModel: MainViewModel): ViewModel
}
- 以上的流程梳理:
- 当
Activity
或者Fragment
需要一个ViewModelProvider.Factory
实例的时候,根据ViewModelModule
中定义的方法,会去寻找AppViewModelFactory
实例作为返回值 -
AppViewModelFactory
的创建需要依赖Map<Class<out ViewModel>, Provider<ViewModel>>
这样一个集合 - 这个集合会由
MainActivityModule
中@IntoMap
标注的方法来提供
- 当
如果你想在项目中集成可能需要用到的代码
-
AppComonent
相关的类,只需要写一次
@Singleton
@Component(
modules = [
AndroidInjectionModule::class,
AppModule::class,
ActivityBindingModule::class,
ViewModelModule::class
]
)
interface AppComponent : AndroidInjector<MainApplication> {
@Component.Factory
interface Factory {
fun create(@BindsInstance application: MainApplication): AppComponent
}
}
@Module
abstract class AppModule
@Module
abstract class ActivityBindingModule {
@ActivityScope
@ContributesAndroidInjector(modules = [MainActivityModule::class])
internal abstract fun mainActivity(): MainActivity
}
@Module
@Suppress("UNUSED")
abstract class ViewModelModule {
@Binds
internal abstract fun bindViewModelFactory(factory: AppViewModelFactory): ViewModelProvider.Factory
}
- 每当你创建一个新的
Activity
需要注入ViewModel
的时候(Fragment
类似) -
viewModelProvider
是一个顶级函数,可以抽到一个工具类中
inline fun <reified T: ViewModel> AppCompatActivity.viewModelProvider(provider: ViewModelProvider.Factory): T {
return ViewModelProviders.of(this, provider).get(T::class.java)
}
@Module
abstract class NewActivityModule {
@Binds
@IntoMap
@ViewModelKey(NewViewModel::class)
abstract fun viewModel(viewModel: NewViewModel): ViewModel
}
class NewActivity() {
@Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
private lateinit var viewModel: MainViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = viewModelProvider(viewModelFactory)
...
}
}
class NewViewModel : ViewModel()
- 注解
@Target(
AnnotationTarget.FUNCTION,
AnnotationTarget.PROPERTY_GETTER,
AnnotationTarget.PROPERTY_SETTER
)
@Retention(AnnotationRetention.RUNTIME)
@MapKey
annotation class ViewModelKey(val value: KClass<out ViewModel>)