Kotlin 中使用 Hilt 的开发实践
Hilt https://developer.android.google.cn/training/dependency-injection/hilt-android Dagger https://developer.android.google.cn/training/dependency-injection/dagger-basics
配置 Hilt
Gradle Build Setup
https://dagger.dev/hilt/gradle-setup
@HiltAndroidApp
class App : Application()
定义并且注入依赖项
当您写代码用到依赖项注入的时候,有两个要点需要考虑:
您需要注入依赖项的类;
可以作为依赖项进行注入的类。
而上述这两点并不互斥,而且在很多情况下,您的类既可以注入依赖项同时也包含依赖。
使依赖项可注入
如果需要在 Hilt 中使某个类变得可注入,您需要告诉 Hilt 如何创建该类的实例。该过程叫做绑定 (bindings)。
在构造函数上添加 @Inject 注解;
在模块上使用 @Binds 注解;
在模块上使用 @Provides 注解。
任何类的构造函数都可以添加 @Inject 注解,这样该类在整个工程中都可以作为依赖进行注入。
class OatMilk @Inject constructor() {
...
}
⮕ 使用模块
Hilt 模块 https://dagger.dev/hilt/modules
Hilt 组件
https://dagger.dev/hilt/components.html
interface Milk { ... }
class OatMilk @Inject constructor(): Milk {
...
}
@Module
@InstallIn(ActivityComponent::class)
abstract class MilkModule {
@Binds
abstract fun bindMilk(oatMilk: OatMilk): Milk
}
@Module
@InstallIn(ApplicationComponent::class)
object ConnectivityManagerModule {
@Provides
fun provideConnectivityManager(
@ApplicationContext context: Context
) = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
}
注入依赖
当依赖可注入后,您可以使用 Hilt 通过两种方式:
作为构造函数的参数注入; 作为字段注入。
⮕ 作为构造函数参数注入
interface Milk { ... }
interface Coffee { ... }
class Latte @Inject constructor(
private val Milk milk,
private val Coffee coffee
) {
...
}
如果构造函数使用了注解 @Inject,Hilt 会根据您为类型所定义的绑定来注入所有的参数。
⮕ 作为字段注入
interface Milk { ... }
interface Coffee { ... }
@AndroidEntryPoint
class LatteActivity : AppCompatActivity() {
@Inject lateinit var milk: Milk
@Inject lateinit var coffee: Coffee
...
}
其它重要的概念
入口点
入口点 https://dagger.dev/hilt/entry-points.html
Activity Fragment
View
Service
BroadcastReceiver
Android 入口点 https://dagger.dev/hilt/android-entry-point.html
@AndroidEntryPoint
class LatteActivity : AppCompatActivity() {
...
}
⮕ 其它入口点
https://dagger.dev/hilt/entry-points.html
ViewModel
interface Milk { ... }
interface Coffee { ... }
@HiltViewModel
class LatteViewModel @Inject constructor(
private val milk: Milk,
private val coffee: Coffee
) : ViewModel() {
...
}
@AndroidEntryPoint
class LatteActivity : AppCompatActivity() {
private val viewModel: LatteViewModel by viewModels()
...
}
SavedStateHandle
https://developer.android.google.cn/reference/androidx/lifecycle/SavedStateHandle
@HiltViewModel
class LatteViewModel @Inject constructor(
@Assisted private val savedState: SavedStateHandle,
private val milk: Milk,
private val coffee: Coffee
) : ViewModel() {
...
}
https://developer.android.google.cn/training/dependency-injection/hilt-jetpack
组件
Hilt 组件
https://dagger.dev/hilt/components.html
作用域
@Module
@InstallIn(ActivityComponent::class)
abstract class MilkModule {
@ActivityScoped
@Binds
abstract fun bindMilk(oatMilk: OatMilk): Milk
}
@AndroidEntryPoint
class LatteActivity : AppCompatActivity() {
@Inject lateinit var milk: Milk
@Inject lateinit var moreMilk: Milk //这里的实例和上面的相同
...
}
// Milk 实例的创建会在 Fridge 存在之前,因为它被绑定到了 activity 的生命周期中
class Fridge @Inject constructor(private val Milk milk) { ... }
@AndroidEntryPoint
class LatteActivity : AppCompatActivity() {
// 下面四项共享了同一个 Milk 实例
@Inject lateinit var milk: Milk
@Inject lateinit var moreMilk: Milk
@Inject lateinit var fridge: Fridge
@Inject lateinit var backupFridge: Fridge
...
}
https://dagger.dev/hilt/components.html
Provider 注入
https://dagger.dev/api/latest/dagger/Provider.html
class Spices @Inject constructor() { ... }
class Latte @Inject constructor(
private val spiceProvider: Provider<Spices>
) {
fun addSpices() {
val spices = spiceProvider.get()// 创建 Spices 的新实例
...
}
}
provider 注入可以忽略具体的依赖类型以及注入的方式。任何可被注入的内容均可以封装在 Provider<...> 中来使用 provider 注入的方式。
依赖注入框架 (像 Dagger 和 Guice) 通常被用于大型且复杂的项目。而 Hilt 既容易上手,配置起来又非常简单,同时作为独立的代码包,还兼顾了 Dagger 中可被各种类型应用,无论代码规模大小,均可兼容的强大特性。
Guice https://github.com/google/guice
免费中文系列课程下载
系统地学习使用 Kotlin 进行 Android 开发
☟ 即刻了解课程详情 ☟
推荐阅读