HILT : 在ViewModel中,lateinit属性库没有被初始化

13 人关注

我在使用HILT的多模块安卓项目中面临这个问题。

 kotlin.UninitializedPropertyAccessException: lateinit property repository has not been initialized in MyViewModel

我的模块是

  • App Module
  • Viewmodel module
  • UseCase Module
  • DataSource Module
  • '应用模块'。

    @AndroidEntryPoint
    class MainFragment : Fragment() {
    private lateinit var viewModel: MainViewModel
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View {
        return inflater.inflate(R.layout.main_fragment, container, false)
    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
        viewModel.test()
    

    'ViewModel Module'

    class MainViewModel @ViewModelInject constructor(private val repository: MyUsecase): ViewModel() {
    fun test(){
        repository.test()
    

    'UseCase模块'

    class MyUsecase @Inject constructor() {
    @Inject
    lateinit var feature: Feature
    fun doThing() {
        feature.doThing()
    @Module
    @InstallIn(ApplicationComponent::class)
    object FeatureModule {
        @Provides
        fun feature(realFeature: RealFeature): Feature = realFeature
    

    '数据源模块'

    interface Feature {
    fun doThing()
    class RealFeature : Feature {
    override fun doThing() {
        Log.v("Feature", "Doing the thing!")
    

    MyFragment ---> MyViewModel ---> MyUseCase ---> DataSource

    我做错了什么,请纠正。

    5 个评论
    你是否尝试过将构造函数注入与 @ViewModelInject 结合使用?
    更确切地说,应该只是把资源库变量放在你的构造函数中。我猜想@ViewModelInject只支持构造函数注入。
    Arun
    @EpicPandaForce 你试过了。它不起作用。 "有时一个类型不能被构造器注入。这可能是由于多种原因发生的。例如,你不能对一个接口进行构造函数注入。你也不能对一个你不拥有的类型进行构造函数注入,比如一个来自外部库的类。在这些情况下,你可以通过使用Hilt模块向Hilt提供绑定信息。" 这个唯一的文件说
    Sounds like MyUsecase is missing @Inject constructor
    你不能构造器注入一个接口,因为一个接口无论如何都不能被构造。在这种情况下,你拥有实际的实现,它可以被构造函数注入。是的,在某些情况下,字段注入是唯一的答案,但目前你的例子不需要它,你应该转向构造函数注入。
    android
    dagger-2
    dagger-hilt
    Arun
    Arun
    发布于 2020-07-10
    6 个回答
    Mosayeb Masoumi
    Mosayeb Masoumi
    发布于 2022-03-07
    已采纳
    0 人赞同

    在你的活动类上面,你必须添加注解 @AndroidEntryPoint 的注释,如下所示。

    @AndroidEntryPoint class MainActivity : AppCompatActivity() {

    manuelvicnt
    manuelvicnt
    发布于 2022-03-07
    0 人赞同

    代码中的问题是, @ViewModelInject 并不像 @Inject 在其他类中那样工作。你不能在ViewModel中执行字段注入。

    You should do:

    class MainViewModel @ViewModelInject constructor(
      private val myUseCase: MyUsecase
    ): ViewModel() {
      fun test(){
        myUseCase.test()
    

    考虑对MyUsecase类采取同样的模式。依赖关系应该在构造函数中传递,而不是在类主体中@Inject。这有点违背了依赖注入的目的。

    Arun
    谢谢你的答复。现在有另一个问题。根据我的例子,我无法在 "MyUsecase "类中注入 "Feature "接口,它说Feature类不能访问。
    在这一点上,你需要把一些东西上传到github,因为这是一个gradle的问题,而不是dagger的问题,而且超出了原来问题的范围。
    @ViewModelInject 已被废弃。请使用 @HiltViewModel 代替。更多细节请点击这里。 stackoverflow.com/q/66185820/14230540
    David Liu
    David Liu
    发布于 2022-03-07
    0 人赞同

    除了将你所有的东西转移到构造函数注入外,你的 RealFeature 也没有被注入,因为你是手动实例化它,而不是让Dagger为你构造它。注意你的FeatureModule是如何直接调用RealFeature的构造函数,并为 @Provides 方法返回它的。Dagger会按原样使用这个对象,因为它认为你已经为它做了所有的设置。只有当你让Dagger构造它时,字段注入才会起作用。

    你的FeatureModule应该看起来像这样。

    @Module
    @InstallIn(ApplicationComponent::class)
    object FeatureModule {
        @Provides
        fun feature(realFeature: RealFeature): Feature = realFeature
    

    或者用@Binds的注释。

    @Module
    @InstallIn(ApplicationComponent::class)
    interface FeatureModule {
        @Binds
        fun feature(realFeature: RealFeature): Feature
    

    这也凸显了为什么你应该转向构造函数注入;有了构造函数注入,这个错误就不可能发生了。

    Arun
    更新了我的代码。 现在 "无法访问RealFeature类"。 如果我使用api而不是实现,那么它就能正常工作。@manuelvicnt说HILT只支持多模块的api。
    @Arun 这里没有足够的资料来诊断gradle的问题。我需要看到整个项目的情况。
    Samir Spahic
    Samir Spahic
    发布于 2022-03-07
    0 人赞同

    首先,我认为你的RealFeature类上缺少 @Inject ,所以Hilt知道如何注入依赖关系。第二,如果你想注入一个不属于Hilt支持的入口点的类,你需要为该类定义你自己的入口点。

    除了你用 @Provides 方法编写的模块外,你还需要告诉Hilt如何访问这个依赖关系。

    在你的情况下,你应该尝试这样的方法。

    @EntryPoint
    @InstallIn(ApplicationComponent::class)
    interface FeatureInterface {
        fun getFeatureClass(): Feature
    

    然后,当你想使用它时,写下这样的内容。

            val featureInterface =
            EntryPoints.get(appContext, FeatureInterface::class.java)
            val realFeature = featureInterface.getFeatureClass()
    

    你可以在这里找到更多信息。

    https://dagger.dev/hilt/entry-points

    https://developer.android.com/training/dependency-injection/hilt-android#not-supported

    Medha
    Medha
    发布于 2022-03-07
    0 人赞同
    class MainViewModel @ViewModelInject constructor(private val repository: HomePageRepository,
                                                     @Assisted private val savedStateHandle: SavedStateHandle)
    : ViewModel(){}
    

    而不是像这样初始化viewmodel。 private lateinit var viewModel:主视图模型 viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)

    Use this directly :

    private val mainViewModel:MainViewModel by activityViewModels()

    EXplanation : 协助保存的状态手柄:将确保如果活动/片段被注解为@Android入口点并结合视图模型注入,它将自动注入来自相关组件活动/应用程序的所有必要的构造函数依赖,这样我们就不必在片段/活动中初始化视图模型时传递这些参数。

    0 人赞同

    确保你添加了类路径和插件

    classpath 'com.google.dagger:hilt-android-gradle-plugin:2.35'