<
Android 依赖项注入 hilt
>
上一篇

学习笔记 Springboot 各层的含义和职责
下一篇

学习笔记 Swagger

Hilt

官方文档

mvn hilt

IOC(Inversion of Control, 控制反转), 一种编程思路

DI(Dependency Injection, 依赖注入), IOC 的具体实现


关于注入的种类

注解

组件注解

定义安装模块

依赖提供注解

作用域注解,配合上面的依赖提供注解使用

注入注解

标记注解

实践

1. 添加 Hilt 依赖

build.gradle.kts [project]

plugin {
    id("com.google.dagger.hilt.android") version "2.44" apply false
}

build.gradle.kts [app]

plugins {
    id("com.google.devtools.ksp")
    id("com.google.dagger.hilt.android")
}

// Allow references to generated code
kapt {
    correctErrorTypes = true
}

dependencies {
    // hilt
    // https://developer.android.com/training/dependency-injection/hilt-android?hl=zh-cn#kts
    implementation("com.google.dagger:hilt-android:2.44")
    kapt("com.google.dagger:hilt-android-compiler:2.44")
}

2. 必须在 Application 设置 Hilt

@HiltAndroidApp
class App : Application() {}

3. 创建依赖模块,使用 @Module 和 @InstallIn

创建一个 Hilt 模块,使用 @Module 和 @InstallIn 注解来提供依赖项

@Module
@InstallIn(SingletonComponent::class) // 可在应用的整个生命周期中使用的依赖
object AppModule {
    @Provides
    @Singleton
    fun provideMyService(): MyService {
        return MyServiceImpl()
    }
}

这里会把方法返回的类型添加到容器中,之后使用 @Inject 可以自动注入该类型

4. 注入依赖,使用 @AndroidEntryPoint 和 @Inject

使用 @AndroidEntryPoint 注解来标记 Activity,然后通过 @Inject 注入依赖

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    @Inject
    lateinit var myService: MyService

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

        // 使用 myService
    }
}

Hilt 目前支持标记以下 Android 类:

@HiltViewModel
class MyViewModel @Inject constructor(
    private val myRepository: MyRepository
) : ViewModel() {
    // ViewModel 逻辑
}

如果您使用 @AndroidEntryPoint 为某个 Android 类添加注解,则还必须为依赖于该类的 Android 类添加注解。 例如,如果您为某个 fragment 添加注解,则还必须为使用该 fragment 的所有 activity 添加注解

至此,可以满足基本使用

5. 测试中的 Hilt

@HiltAndroidTest
class MyActivityTest {

    @get:Rule
    var hiltRule = HiltAndroidRule(this)

    @Before
    fun init() {
        hiltRule.inject()
    }

    @Test
    fun testMyActivity() {
        // 测试逻辑
    }
}

Module 类

只要在 @Module 的类中的某个带返回类型的方法进行 @Provides 提供实例,然后在别的地方就能使用 @Inject 自动注入这个返回类型的实例

不加作用域注解,注入对象时就不会是单例

示例

@Module
@InstallIn(ActivityRetainedComponent::class) // 生命周期,可选其一
object MainModule {
    // Provides 用在手动创建对象的情况
    @Provides
    // 声明作用域 需要与生命周期对应 否则报错
	// 不加作用域注解则注入时的实例不是单例
    @Singleton // 作用域,可选其一
    fun getUser(): User {
        return User(1, "ZhangSan")
    }
}

@Module
@InstallIn(ActivityRetainedComponent::class)
abstract class MainModule {
    // Binds 用在传入实现类时的情况
    @Binds
    @ActivityRetainedScoped
    abstract fun login(login: LoginImpl) : ILogin
}

data class User(
    var id: Int,
    var username: String
)

使用 注入对象,也可在构造函数使用

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    @Inject
    lateinit var user: User
}

class UserRemoteSource @Inject constructor(
    private val apiService: ApiService // Hilt 自动注入 ApiService 实例
) {
    suspend fun fetchUser(): User {
        return apiService.getUser() // 通过 API 获取用户数据
    }
}

解惑

有参构造函数:Hilt 自动处理,通过 @Inject 注解标记,简化了依赖的管理。 无参构造函数:Hilt 无法自动管理,因此需要显式提供方法来创建实例。

通常建议使用有参构造函数来充分利用 Hilt 的依赖注入特性,这样可以减少手动管理依赖的需要

Q1:

@Module
@InstallIn(ActivityComponent::class)
object MyModule {
    @Provides
    @Singleton
    fun provideSharedRepository(): SharedRepository {
        return SharedRepository()
    }
}

A1: 生命周期是 Activity,作用域 单例。会在每个使用的 Activity 都生成一个单例,生命周期跟随 Activity

Q2:

@Module
@InstallIn(SingletonComponent::class)
object AppModule {
    @Provides
    @ActivityScoped
    fun provideSharedRepository(): SharedRepository {
        return SharedRepository()
    }
}

A2: 报错,两者作用域是互相冲突的,需要 ActivityComponent + ActivityScoped
还有其它一些会冲突的情况,例如
ActivityComponent + ServiceScoped (因为 Activity 的生命周期与 Service 的生命周期不同)
ActivityComponent + ViewModelScoped
ActivityComponent + FragmentScoped
SingletonComponent + ActivityScoped
等,
需要选择适当的作用域与安装组件,避免以上列举的冲突组合

Top
Foot