匕首2:多模块项目,注入依赖项,但在运行时出现“ lateinit属性存储库尚未初始化”错误

里姆

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(如上所述,MyRepositorycore模块中)

这里是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] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

Related 相关文章

热门标签

归档