文章目录
- Jetpack Hilt
- 概述
- 配置Hilt
- Hilt API介绍
- 常用注解说明
- 入口点
- 组件层次结构
- 为Android类生成的组件
- 组件的作用域
- 组件的生命周期
- 组件的默认绑定
- Hilt 使用
- 配置Application
- 在Activity中使用
- 带参数的依赖注入
- 接口的依赖注入
- 相同类型注入不同的对象
- 第三方类的依赖注入
- Application & Context
- 内置依赖Application和Activity
- 依赖注入自定义Application
- 在ViewModel中使用
- 代码下载
Jetpack Hilt
概述
- 依赖注入指Dependency Injection,简称DI,Hilt是一个功能强大用法简单的依赖注入框架。
- 通俗来讲,一个类中有一个变量,这个变量就是类的依赖,通过外部注入对这个变量进行赋值,这就是依赖注入。
- 依赖注入有利于代码解耦。
配置Hilt
project/build.gradle
buildscript {
ext {
kotlin_version = '1.5.21'
hiltVersion = "2.36"
}
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.2.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// Hilt
classpath "com.google.dagger:hilt-android-gradle-plugin:$hiltVersion"
}
}
module/build.gradle
apply plugin: 'kotlin-kapt'
apply plugin: 'dagger.hilt.android.plugin'
android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
implementation "com.google.dagger:hilt-android:$hiltVersion"
kapt "com.google.dagger:hilt-android-compiler:$hiltVersion"
}
Hilt API介绍
常用注解说明
@HiltAndroidApp
- 必须定义一个由 @HiltAndroidApp 注解的 Application。
- 在编译期会触发 Hilt 的代码生成。
@AndroidEntryPoint
- @AndroidEntryPoint 注解提供Android类的依赖。
@Inject
- 在变量上添加 @Inject 注解,表示将实例注入到变量,在 super.onCreate() 之后被调用。
- 在类的构造函数上添加 @Inject 注解,表示告诉 Hilt 怎么创建该类的实例。
@Module
- 使用 @Module 注解时必须使用 @InstallIn 注解指定活动范围。
- 用于创建依赖类的对象。
@Provides
- 配合 @Module 注解使用。
- 用于提供依赖项对象。
@EntryPoint
Hilt 支持最常见的 Android 类 Application、Activity、Fragment、View、Service、BroadcastReceiver 等等,但是您可能需要在Hilt 不支持的类中执行依赖注入,在这种情况下可以使用 @EntryPoint 注解进行创建,Hilt 会提供相应的依赖。
入口点
Hilt 支持的入口点:
- Application
- ViewModel
- Activity
- Fragment
- View
- Service
- BroadcastReceiver
说明:Application 的入口点需要使用 @HiltAndroidApp 注解,ViewModel 的入口点需要使用 @HiltViewModel 注解,其他入口点使用 @AndroidEntryPoint 注解。
组件层次结构
Hilt 为绝大多数 Android 框架类生成组件 (或称为依赖项容器)。每个组件关联信息 (或称为绑定) 通过组件层次结构向下传递。
为Android类生成的组件
在 @InstallIn 注解中使用。
Hilt 组件 | 注入器面向的对象 |
|
|
| 不适用 |
|
|
|
|
|
|
|
|
| 带有 |
|
|
组件的作用域
- @Singleton 注解就是一个作用域注解,表示在整个应用程序范围内共享实例。
- @ActivityScoped 注解表示在Activity和内部的Fragment和View共享实例。
Android 类 | 生成的组件 | 作用域 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
带有 |
|
|
|
|
|
组件的生命周期
Hilt 会按照相应 Android 类的生命周期自动创建和销毁生成的组件类的实例。
生成的组件 | 创建时机 | 销毁时机 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
组件的默认绑定
每个 Hilt 组件都附带一组默认绑定,Hilt 可以将其作为依赖项注入您自己的自定义绑定。请注意,这些绑定对应于常规 activity 和 fragment 类型,而不对应于任何特定子类。这是因为,Hilt 会使用单个 activity 组件定义来注入所有 activity。每个 activity 都有此组件的不同实例。
Android 组件 | 默认绑定 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Hilt 使用
配置Application
定义一个 Application 并使用 @HiltAndroidApp 注解,否则 Hilt 将无法使用。
@HiltAndroidApp
class BaseApplication : Application() {
}
注册到 AndroidManifest.xml 文件中:
<application
android:name=".BaseApplication">
</application>
在Activity中使用
- 使用 @AndroidEntryPoint 注解,将依赖项注入 Android 类。
- 使用 @Inject 注解的字段不能为private。
使用
class User @Inject constructor() {
fun sayHello() {
log("hello")
}
}
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject
lateinit var user: User
@Inject
lateinit var user2: User
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
log("$user")
log("$user2")
user.sayHello()
user2.sayHello()
}
}
输出信息:
com.example.hiltdemo.entity.User@6a0ef50
com.example.hiltdemo.entity.User@3507549
hello
hello
带参数的依赖注入
定义Driver类,并在构造函数上使用 @Inject 注解。
在Car类的构造函数里增加 Driver 参数。
class Driver @Inject constructor() {
}
class Car @Inject constructor(val driver: Driver) {
fun start() {
log("$driver 正在开车")
}
}
使用:
@Inject
lateinit var car: Car
car.start()
接口的依赖注入
- 定义一个Engine接口。
- 实现两个类GasEngine、ElectricEngine。
- 使用 @Module 注解声明一个模块类EngineModule,使用 @Binds 注解提供依赖注入的对象。
定义接口:
interface Engine {
fun start()
fun shutdown()
}
实现接口:
class GasEngine @Inject constructor() : Engine {
override fun start() {
log("燃油引擎 start")
}
override fun shutdown() {
log("燃油引擎 shutdown")
}
}
class ElectricEngine @Inject constructor() : Engine {
override fun start() {
log("电力引擎 start")
}
override fun shutdown() {
log("电力引擎 shutdown")
}
}
定义Module:
返回值必须是Engine类型,表示给Engine类型的接口提供实例。
@Module
@InstallIn(ActivityComponent::class)
abstract class EngineModule {
@Binds
abstract fun bindEngine(gasEngine: GasEngine): Engine
}
使用:
class Car @Inject constructor(val user: User) {
@Inject
lateinit var engine: Engine
fun start() {
engine.start()
log("$driver 正在开车")
engine.shutdown()
}
}
输出信息:
燃油引擎 start
Driver@e7744ba 正在开车
燃油引擎 shutdown
相同类型注入不同的对象
- 使用 @Qualifier 注解分别定义2个注解:BindGasEngine、BindElectricEngine。
- 定义一个Module类,分别添加2个注解。
- 在注入的地方使用2个注解。
定义两个注解:
用于区分类型。
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class BindGasEngine
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class BindElectricEngine
定义Module:
@Module
@InstallIn(ActivityComponent::class)
abstract class SelectEngineModule {
@BindGasEngine
@Binds
abstract fun bindGasEngine(gasEngine: GasEngine): Engine
@BindElectricEngine
@Binds
abstract fun bindElectricEngine(electricEngine: ElectricEngine): Engine
}
使用:
使用 @BindGasEngine 注解就会注入 GasEngine 类型的实例。
class Car @Inject constructor(val driver: Driver) {
@BindGasEngine
@Inject
lateinit var gasEngine: Engine
@BindElectricEngine
@Inject
lateinit var electricEngine: Engine
fun start() {
gasEngine.start()
electricEngine.start()
log("hello $driver 正在开车")
gasEngine.shutdown()
electricEngine.shutdown()
}
}
输出信息:
燃油引擎 start
电力引擎 start
hello Driver@e7744ba 正在开车
燃油引擎 shutdown
电力引擎 shutdown
第三方类的依赖注入
- 使用 @Module 注解定义Module,并使用 @InstallIn 注解定义安装至哪个组件。
- 使用 @Provides 注解表示提供的实例。
定义Module:
@Single 注解表示单例模式,provideRetrofit() 函数中的参数 Hilt 会自动注入。
@Module
@InstallIn(SingletonComponent::class)
class NetworkModule {
@Singleton
@Provides
fun provideOkHttpClient(): OkHttpClient {
return OkHttpClient.Builder()
.build()
}
@Singleton
@Provides
fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {
return Retrofit.Builder()
.baseUrl("https://wanandroid.com/")
.build()
}
}
使用:
@Inject
lateinit var okHttpClient: OkHttpClient
@Inject
lateinit var retrofit: Retrofit
Application & Context
Hilt 提供了 @ApplicationContext 和 @ActivityContext 内置注解,会自动提供一个 Application 类型和 Activity 类型的 Context 注入。
使用Context类型:
class MyContext @Inject constructor(
@ApplicationContext val appContext: Context,
@ActivityContext val activityContext: Context
)
使用:
@Inject
lateinit var myContext: MyContext
log("appContext : ${myContext.appContext}")
log("activityContext : ${myContext.activityContext}")
输出信息:
appContext : com.example.hiltdemo.BaseApplication@e7744ba
activityContext : com.example.hiltdemo.MainActivity@756b7ec
内置依赖Application和Activity
对于 Application 和 Activity 这两个类型,Hilt 也是给它们预置好了注入功能,可以省略注解。
class MyAppAndActivity @Inject constructor(
val application: Application,
val activity: Activity
)
使用:
@Inject
lateinit var my: MyAppAndActivity
log("application : ${my.application}")
log("activity : ${my.activity}")
输出信息:
application : com.example.hiltdemo.BaseApplication@e7744ba
activity : com.example.hiltdemo.MainActivity@756b7ec
依赖注入自定义Application
定义Module:
@Module
@InstallIn(SingletonComponent::class)
class ApplicationModule {
@Provides
fun provideBaseApplication(application: Application): BaseApplication {
return application as BaseApplication
}
}
使用:
@Inject
lateinit var baseApplication: BaseApplication
log("baseApplication: $baseApplication")
输出信息:
baseApplication: com.example.hiltdemo.BaseApplication@e7744ba
在ViewModel中使用
- MVVM架构中,数据层层的实例应该由ViewModel层创建。
- ViewModel层需要使用 @HiltViewModel 注解,在ViewModel的构造函数上使用 @Inject 注解。
数据层:
在构造函数上使用 @Inject 注解,表示将 Repository 实例注入到 ViewModel中。
class Repository @Inject constructor() {
fun getData() = "从仓库获取了数据"
}
ViewModel层:
@HiltViewModel
class MyViewModel @Inject constructor(
private val savedStateHandle: SavedStateHandle,
private val repository: Repository
) : ViewModel() {
private val liveData = MutableLiveData<String>()
fun loadData(): MutableLiveData<String> {
viewModelScope.launch {
delay(5000L)
liveData.value = repository.getData()
}
return liveData
}
}
使用:
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private val viewModel by viewModels<MyViewModel>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewModel.loadData().observe(this) {
log(it)
}
}
}
代码下载