獲取 ViewModel
實(shí)例的一般做法
- 首先會創(chuàng)建一個
MainViewModel
類,然后在類中定義一個繼承自ViewModelProvider.Factory
接口的類,實(shí)現(xiàn) create
接口,直接通過MainViewModel
的構(gòu)造方法創(chuàng)建了一個實(shí)例
- 然后在
Activity
或者Fragment
中, 通過ViewModelProviders.of(this, MainViewModel.Factory()).get(MainViewModel::class.java)
來獲取viewModel 的實(shí)例對象
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
類的實(shí)現(xiàn)都是相同的
- 如果項(xiàng)目十分龐大的話吨岭,必然會產(chǎn)生巨量的樣板代碼(
Boilerplate code
),這給我們的維護(hù)會造成困難
使用 dagger-android 來減少 boilerplate
- 首先可以先看一下使用
Dagger
如何減少樣板代碼的實(shí)現(xiàn)
- 可以先從具體需要注入
ViewModel
的MainActivity
地方來看
- 僅僅注入了一個
ViewModelProvider.Factory
接口的一個實(shí)例峦树,然后就可以通過相應(yīng)的方法來獲取 viewModel
的實(shí)例對象
- 在
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
的實(shí)例來作為依賴的提供者辣辫,而AppViewModelFactory
的實(shí)例會通過其構(gòu)造方法來創(chuàng)建
- 至此
ViewModelProvider.Factory
這個接口的注入已經(jīng)完善
- 下面會詳細(xì)的描述
AppViewModelFactory
實(shí)例創(chuàng)建時,其構(gòu)造方法中所需依賴獲取的具體流程
@Module
@Suppress("UNUSED")
abstract class ViewModelModule {
@Binds
internal abstract fun bindViewModelFactory(factory: AppViewModelFactory): ViewModelProvider.Factory
}
- 首先來看一下
ViewModelProvider.Factory
實(shí)現(xiàn)類
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>>
類型的實(shí)例
- 該實(shí)例會通過
MainActivityModule
中通過@IntoMap
標(biāo)注的方法來提供
- 這里涉及到multibindings來提供魁巩,不太懂的同學(xué)可以先去學(xué)習(xí)一下
- 包含該
Module
的 Component
會提供以下兩種Map類型的集合以供使用
Map<Class, ViewModel>
Map<Class, Provider<ViewModel>>
-
Dagger
會通過@IntoMap
創(chuàng)建的 Map<Class, Provider<ViewModel>>
類型的實(shí)例來創(chuàng)建AppViewModelFactory
實(shí)例急灭,來作為ViewModelProvider.Factory
接口的依賴進(jìn)行注入
@Module
abstract class MainActivityModule {
@Binds
@IntoMap
@ViewModelKey(MainViewModel::class)
abstract fun viewModel(viewModel: MainViewModel): ViewModel
}
- 以上的流程梳理:
- 當(dāng)
Activity
或者Fragment
需要一個ViewModelProvider.Factory
實(shí)例的時候,根據(jù)ViewModelModule
中定義的方法谷遂,會去尋找AppViewModelFactory
實(shí)例作為返回值
-
AppViewModelFactory
的創(chuàng)建需要依賴Map<Class<out ViewModel>, Provider<ViewModel>>
這樣一個集合
- 這個集合會由
MainActivityModule
中@IntoMap
標(biāo)注的方法來提供
如果你想在項(xiàng)目中集成可能需要用到的代碼
-
AppComonent
相關(guān)的類葬馋,只需要寫一次
@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
}
- 每當(dāng)你創(chuàng)建一個新的
Activity
需要注入ViewModel
的時候(Fragment
類似)
-
viewModelProvider
是一個頂級函數(shù),可以抽到一個工具類中
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>)