超级 Fragment 或 ViewModel 中的 Hilt 字段注入

Ham*_*ruf 9 android dependency-injection dagger-hilt

我在我的 Android 项目中使用 Dagger-Hilt 进行依赖注入,现在我遇到了这种情况,我有一个基本的抽象 Fragment

基础视图模型.kt

abstract class BaseViewModel constructor(
    val api: FakeApi,
) : ViewModel() {
    
    //...
    
}
Run Code Online (Sandbox Code Playgroud)

在这里,我有一个依赖项是FakeApi. 我想要做的就是注入FakeApiBaseViewModel要在现有的BaseViewModel和所有的孩子。

  • 我尝试的第一种方法是使用构造函数注入并将其注入子进程并使用构造函数将其传递给超级进程。

任务视图模型.kt

@HiltViewModel
class TaskViewModel @Inject constructor(
    api: FakeApi
) : BaseViewModel(api){

}
Run Code Online (Sandbox Code Playgroud)

这种方法工作正常,但我不需要将依赖从类传递childsuper类,我需要FakeApi将 自动注入到 中BaseViewModel而不必传递它,因为我有三个抽象级别(还有另一个类继承自TaskViewModel) 所以我必须通过它两次。

  • 第二种方法是使用场注入如下

基础视图模型.kt

abstract class BaseViewModel: ViewModel() {
    @Inject
    lateinit var api: FakeApi
    //...
}
Run Code Online (Sandbox Code Playgroud)

任务视图模型.kt

@HiltViewModel
class TaskViewModel @Inject constructor(): BaseViewModel() {
    
}
Run Code Online (Sandbox Code Playgroud)

这种方法对我不起作用,FakeApi没有注入,我有一个Exception

kotlin.UninitializedPropertyAccessException: lateinit property api has not been initialized
Run Code Online (Sandbox Code Playgroud)

我的问题是

  • 为什么场注入对我不起作用?
  • 有没有办法对类使用构造函数注入super而不是从child?

Ham*_*ruf 8

感谢这个Github Issue,我发现问题是你不能在 ViewModel 构造函数初始化期间使用字段注入的属性,但在构造函数(包括所有属性直接初始化)初始化后你仍然使用它。

\n

Dagger首先完成构造函数注入过程,然后进行字段注入过程。这就是为什么在构造函数注入完成之前不能使用字段注入。

\n

\xe2\x9d\x8c 错误使用

\n
abstract class BaseViewModel : ViewModel() {\n\n    @Inject\n    protected lateinit var fakeApi: FakeApi\n\n    val temp = fakeApi.doSomething() // Don\'t use it in direct property declaration\n\n    init {\n        fakeApi.doSomething() // Don\'t use it in the init block\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

\xe2\x9c\x94\xef\xb8\x8f 正确使用

\n
abstract class BaseViewModel : ViewModel() {\n\n    @Inject\n    protected lateinit var fakeApi: FakeApi\n\n    val temp: Any\n        get() = fakeApi.doSomething() // Use property getter\n\n    fun doSomething(){\n        fakeApi.doSomething() // Use it after constructor initialization\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

或者您可以使用 来by lazy声明您的属性。

\n


Pha*_*inh 6

我进行了测试,发现现场注入base class仍然适用于 Hilt 2.35。我无法像你一样收到错误,所以也许你可以尝试更改 Hilt 版本或检查你如何提供FakeApi

abstract class BaseViewModel : ViewModel() {

    @Inject
    protected lateinit var fakeApi: FakeApi
}
Run Code Online (Sandbox Code Playgroud)

假API

// Inject constructor also working
class FakeApi {

    fun doSomeThing() {
        Log.i("TAG", "do something")
    }
}
Run Code Online (Sandbox Code Playgroud)

主视图模型

@HiltViewModel
class MainViewModel @Inject constructor() : BaseViewModel() {

    // from activity, when I call this function, the logcat print normally 
    fun doSomeThing() {
        fakeApi.doSomeThing()
    }
}
Run Code Online (Sandbox Code Playgroud)

应用程序模块

@Module
@InstallIn(SingletonComponent::class)
class AppModule {

    @Provides
    fun provideAPI(
    ): FakeApi {
        return FakeApi()
    }
}
Run Code Online (Sandbox Code Playgroud)

https://github.com/PhanVanLinh/AndroidHiltInjectInBaseClass