Dagger2 + Kotlin:lateinit 属性尚未初始化

Posted

技术标签:

【中文标题】Dagger2 + Kotlin:lateinit 属性尚未初始化【英文标题】:Dagger2 + Kotlin: lateinit property has not been initialized 【发布时间】:2018-11-06 08:32:23 【问题描述】:

我正在尝试将 ViewModelFactory 注入到我的 Activity 中,但它一直抛出同样的错误:lateinit 属性 viewModelFactory 尚未初始化。我找不到我可能做错了什么。从我的课程中查看上面的代码

AppComponent.kt

@Component(modules = [(AppModule::class), (NetworkModule::class), (MainModule::class)])
interface AppComponent 

    fun inject(application: TweetSentimentsApplication)

    fun inject(mainActivity: MainActivity)

    fun context(): Context

    fun retrofit(): Retrofit

MainModule.kt

@Module
class MainModule 

    @Provides
    fun mainViewModelFactorty(repository: TweetRepository): MainViewModelFactory = MainViewModelFactory(repository)

    @Provides
    fun local(database: AppDatabase): TweetLocal = TweetLocal(database)

    @Provides
    fun remote(tweetService: TweetService): TweetRemote = TweetRemote(tweetService)

    @Provides
    fun tweetService(retrofit: Retrofit): TweetService = retrofit.create(TweetService::class.java)

    @Provides
    fun repository(local: TweetLocal, remote: TweetRemote): TweetRepository = TweetRepository(local, remote)


MainActivity.kt

class MainActivity : AppCompatActivity() 

    @Inject lateinit var viewModelFactory: MainViewModelFactory

    private val viewModel: MainViewModel? = null

    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main_activity)

        ViewModelProviders.of(this, viewModelFactory).get(MainViewModel::class.java)

        viewModel?.init("guuilp")
        viewModel?.getTweetList()?.observe(this, Observer 
            Toast.makeText(this, it?.size.toString(), Toast.LENGTH_LONG).show()
        )
    

TweetSentimentsApplication.kt

open class TweetSentimentsApplication: Application()

    companion object 
        lateinit var appComponent: AppComponent
    

    override fun onCreate() 
        super.onCreate()

        initDI()
    

    private fun initDI() 
        appComponent = DaggerAppComponent.builder()
                .appModule(AppModule(this))
                .build()
    

【问题讨论】:

【参考方案1】:

当你初始化你的MainActivity时,你必须调用你在AppComponent中定义的inject(mainActivity: MainActivity)方法,这就是Dagger实际注入你需要的依赖的方式。

override fun onCreate(savedInstanceState: Bundle?) 
    super.onCreate(savedInstanceState)
    setContentView(R.layout.main_activity)

    // This is where the dependencies are injected
    TweetSentimentsApplication.appComponent.inject(this)

    ViewModelProviders.of(this, viewModelFactory).get(MainViewModel::class.java)

    ...

【讨论】:

谢谢!我正在学习 Dagger,有些东西还不是很清楚。您的回答和解释帮助很大! 查看我的教程系列,从这里开始:dev.to/autonomousapps/… 实际上根据文档,这是不正确的。 Dagger 假设在不需要您执行“注入”的情况下注入依赖项 - “如果您的类具有 Inject-annotated 字段但没有 Inject-annotated 构造函数,则 Dagger 将在请求时注入这些字段,但不会创建新实例。添加 no - 带有 @Inject 注释的参数构造函数,以指示 Dagger 也可以创建实例。” - Kotlin 似乎违反了此声明。 @androidDev Dagger 将注入这些字段 IF REQUESTED,也就是当您调用 component.inject(this) 时。 Kotlin 与此无关。 @EpicPandaForce 您所说的仅适用于这种情况下的 Android 组件。这可能意味着 Dagger 不像它宣传的那样运行时安全。虽然它不能为@Injected 字段分配实例,但它仍然允许项目编译【参考方案2】:

另外,请确保您的应用程序名称已添加到 AndroidManifest.xml 文件中。

<application
    android:name=".YourAppName"
    ..../>

【讨论】:

在 dagger2 上花费了一整天的时间来寻找问题之后,我看到了这条评论,并在清单中添加了应用程序类。这是一个愚蠢的错误,但曾经想过 WTF!你怎么猜的???你救了我的大脑!谢谢!【参考方案3】:

您也可以这样做:

  @Inject
  lateinit var viewModelFactory: ViewModelProvider.Factory
  val mainViewModel: MainViewModel by lazy 
      ViewModelProviders.of(this, viewModelFactory)[MainViewModel::class.java]
  

并使用带有@ContributesAndroidInjector 的抽象模块进行活动,以及 视图模型的抽象模块。使用抽象更高效:

   @Module
   abstract class AndroidBindingModule 

   @ContributesAndroidInjector
    internal abstract fun contributesAnActivity(): AnActivity
    



 @Module
    abstract class ViewModelModule 
      //the default factory only works with default constructor
      @Binds
      @IntoMap
      @ViewModelKey(AViewModel::class)
      abstract fun bindArtViewModel(aViewModel: AViewModel): ViewModel

      @Binds
      abstract fun bindViewModelFactory(factory: AViewModelFactory): ViewModelProvider.Factory
    





 @Documented
    @Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
    @Retention(RetentionPolicy.RUNTIME)
    @MapKey
    internal annotation class ViewModelKey(val value: KClass<out ViewModel>)

【讨论】:

【参考方案4】:

您还可以扩展 DaggerAppCompatActivity 来代替 AppCompatActivity。例如

class MainActivity : DaggerAppCompatActivity() 

    @Inject lateinit var viewModelFactory: MainViewModelFactory

    private val viewModel: MainViewModel? = null

    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main_activity)

        ViewModelProviders.of(this, viewModelFactory).get(MainViewModel::class.java)

        viewModel?.init("guuilp")
        viewModel?.getTweetList()?.observe(this, Observer 
            Toast.makeText(this, it?.size.toString(), Toast.LENGTH_LONG).show()
        )
    

【讨论】:

这仅在您设置@ContributesAndroidInjectorHasActivityinjectors 时有效。【参考方案5】:

我的错误是创建一个新对象并从中获取一个组件,例如App().component

所以在这种情况下,您需要将组件字段放入companion object,并将代码替换为App.component

【讨论】:

【参考方案6】:

也许您错过了在片段/活动中实现“可注入”接口。这将片段/活动标记为可注入。

【讨论】:

以上是关于Dagger2 + Kotlin:lateinit 属性尚未初始化的主要内容,如果未能解决你的问题,请参考以下文章

Kotlin Autowired 问题 - lateinit (obv)

Kotlin中,lateinit 和 lazy{} 的区别

Kotlin基础 3.关键字:lateinit(更新中)

Kotlin - lateinit VS 任何? =空

kotlin.UninitializedPropertyAccessException:lateinit 属性尚未初始化

Kotlin基础 关键字:lateinit和by lazy