Dagger版本是2.25.2。
我有两个Android项目模块:core
模块和app
模块。
在core
模块,I为匕首定义CoreComponent
,
在app
模块中,我有AppComponent
匕首。
CoreComponet
在核心项目模块中:
@Component(modules = [MyModule::class])
@CoreScope
interface CoreComponent {
fun getMyRepository(): MyRepository
}
在核心项目模块中,我有一个存储库类,它不属于任何dagger模块,但是我@Inject
在其构造函数旁边使用了注释:
class MyRepository @Inject constructor() {
...
}
我的应用程序组件:
@Component(modules = [AppModule::class], dependencies = [CoreComponent::class])
@featureScope
interface AppComponent {
fun inject(activity: MainActivity)
}
在MainActivity
:
class MainActivity: AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val coreComponent = DaggerCoreComponent.builder().build()
DaggerAppComponent
.builder()
.coreComponent(coreComponent)
.build()
.inject(this)
}
}
我的项目是MVVM体系结构,通常:
MainActivity
主机 MyFragment
MyFragment
有参考 MyViewModel
MyViewModel
具有依赖项MyRepository
(如上所述,MyRepository
在core
模块中)
这里是MyViewModel
:
class MyViewModel : ViewModel() {
// Runtime error: lateinit property repository has not been initialize
@Inject
lateinit var repository: MyRepository
val data = repository.getData()
}
MyViewModel
在MyFragment中初始化:
class MyFragment : Fragment() {
lateinit var viewModel: MyViewModel
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = ViewModelProviders.of(this).get(MyViewModel::class.java)
...
}
}
当我运行我的应用程序时,它因运行时错误而崩溃:
kotlin.UninitializedPropertyAccessException: lateinit property repository has not been initialize
该错误告诉我匕首依赖项注入不适用于我的设置。那么,我想念什么?如何摆脱这个错误?
====更新=====
我试过了 :
class MyViewModel @Inject constructor(private val repository: MyRepository): ViewModel() {
val data = repository.getData()
}
现在,当我运行该应用程序时,出现新错误:
Caused by: java.lang.InstantiationException: class foo.bar.MyViewModel has no zero argument constructor
======更新2 =====
现在,我创建了MyViewModelFactory
:
class MyViewModelFactory @Inject constructor(private val creators: Map<Class<out ViewModel>,
@JvmSuppressWildcards Provider<ViewModel>>): ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
val creator = creators[modelClass] ?: creators.entries.firstOrNull {
modelClass.isAssignableFrom(it.key)
}?.value ?: throw IllegalArgumentException("unknown model class $modelClass")
try {
@Suppress("UNCHECKED_CAST")
return creator.get() as T
} catch (e: Exception) {
throw RuntimeException(e)
}
}
}
我将MyFragment更新为:
class MyFragment : Fragment() {
lateinit var viewModel: MyViewModel
@Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
override fun onAttach(context: Context) {
// inject app component in MyFragment
super.onAttach(context)
(context.applicationContext as MyApplication).appComponent.inject(this)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// I pass `viewModelFactory` instance here, new error here at runtime, complaining viewModelFactory has not been initialized
viewModel = ViewModelProviders.of(this, viewModelFactory).get(MyViewModel::class.java)
...
}
}
现在我运行我的应用程序,出现新错误:
kotlin.UninitializedPropertyAccessException: lateinit property viewModelFactory has not been initialized
还缺少什么?
为了注入依赖关系,Dagger必须是:
DaggerAppComponent
.builder()
.coreComponent(coreComponent)
.build()
.inject(this)
在您的第一种方法中,以上都不是正确的,MyViewModel
在Dagger的控制范围之外创建了一个新实例:
viewModel = ViewModelProviders.of(this).get(MyViewModel::class.java)
因此,依赖项甚至都没有初始化。此外,即使您要像在活动中那样更手动地执行注入操作,代码也仍然会失败,因为您试图repository
在对象的初始化过程中尝试引用该属性val data = repository.getData()
,然后lateinit var
才能设置该属性。在这种情况下,lazy
代表很方便:
class MyViewModel : ViewModel() {
@Inject
lateinit var repository: MyRepository
val data by lazy { repository.getData() }
...
}
但是,场注入并不是执行DI的最理想方法,尤其是当可注入对象需要了解它时。您可以ViewModel
使用构造注入将依赖项注入,但是需要一些额外的设置。
问题在于Android SDK创建和管理视图模型的方式。它们使用a创建ViewModelProvider.Factory
,默认视图要求视图模型具有非参数构造函数。因此,执行构造函数注入所需要做的主要是提供您的自定义ViewModelProvider.Factory
:
// injects the view model's `Provider` which is provided by Dagger, so the dependencies in the view model can be set
class MyViewModelFactory<VM : ViewModel> @Inject constructor(
private val viewModelProvider: @JvmSuppressWildcards Provider<VM>
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>): T =
viewModelProvider.get() as T
}
(实现自定义有两种方法ViewModelProvider.Factory
,第一种使用单例工厂,该工厂获取所有视图模型的映射Provider
,后一种(上面的一种)为每个视图模型创建一个工厂。我更喜欢第二种因为它不需要额外的样板并绑定Dagger模块中的每个视图模型。)
在视图模型中使用构造函数注入:
class MyViewModel @Inject constructor(private val repository: MyRepository): ViewModel() {
val data = repository.getData()
}
然后将工厂注入您的活动或片段中,并使用它来创建视图模型:
@Component(modules = [AppModule::class], dependencies = [CoreComponent::class])
@featureScope
interface AppComponent {
fun inject(activity: MainActivity)
fun inject(fragment: MyFragment)
}
class MyFragment : Fragment() {
@Inject
lateinit var viewModelFactory: MyViewModelFactory<MyViewModel>
lateinit var viewModel: MyViewModel
override fun onAttach(context: Context) {
// you should create a `DaggerAppComponent` instance once, e.g. in a custom `Application` class and use it throughout all activities and fragments
(context.applicationContext as MyApp).appComponent.inject(this)
super.onAttach(context)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = ViewModelProviders.of(this, viewModelFactory)[MyViewModel::class.java]
...
}
}
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句