Jetpack Hilt依赖注入
Android 依赖项注入
什么是依赖项注入
类通常需要引用其他类。例如,Car
类可能需要引用 Engine
类。这些必需类称为依赖项,在此示例中,Car
类依赖于拥有 Engine
类的一个实例才能运行。
类可通过以下三种方式获取所需的对象:
- 类构造其所需的依赖项。在以上示例中,
Car
将创建并初始化自己的Engine
实例。 - 从其他地方抓取。某些 Android API(如
Context
getter 和getSystemService()
)的工作原理便是如此。 - 以参数形式提供。应用可以在构造类时提供这些依赖项,或者将这些依赖项传入需要各个依赖项的函数。在以上示例中,
Car
构造函数将接收Engine
作为参数。
第三种方式就是依赖项注入!使用这种方法,您可以获取并提供类的依赖项,而不必让类实例自行获取。
而Hilt可以提供自动的依赖项注入而不用自己手动去new
举个例子:
在 Android 开发者官网有一张关于 MVVM 架构的示意图,如下图所示:
一般我们自己去实现MVVM架构时,需要在Activity中创建ViewModel的实例,在ViewModel中创建Repository的实例,而使用Hilt后,就可以让Hilt自动为我们提供这个依赖项的实例
引入Hilt
第一步,在项目根目录的 build.gradle 文件中配置 Hilt 的插件路径:
buildscript {
...
dependencies {
...
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.28-alpha'
}
}
接下来,在 app/build.gradle 文件中,引入 Hilt 的插件并添加 Hilt 的依赖库:
...
apply plugin: 'kotlin-kapt'
apply plugin: 'dagger.hilt.android.plugin'
dependencies {
implementation "com.google.dagger:hilt-android:2.28-alpha"
kapt "com.google.dagger:hilt-android-compiler:2.28-alpha"
}
复制代码
这里同时还引入了 kotlin-kapt 插件,是因为 Hilt 是基于编译时注解来实现的,而启用编译时注解功能一定要先添加 kotlin-kapt 插件。如果用 Java 开发项目,可以不引入这个插件,同时将添加注解依赖库时使用的 kapt 关键字改成 annotationProcessor 即可。
最后在当前项目中启用 Java 8 的功能,编辑 app/build.gradle 文件,并添加如下内容:
android {
...
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
简单使用
1.自定义一个 Application 加上一个 @HiltAndroidApp
@HiltAndroidApp
class LogApplication : Application() {}
将 MyApplication 注册到 AndroidManifest.xml 文件
2.在要使用依赖注入的Activity或Fragment上加上@AndroidEntryPoint注解
3.给要依赖注入的对象添加@Inject注解
//在后台,Hilt 将使用自动生成的 LogsFragment 依赖项容器中内置的实例在 onAttach() 生命周期方法中填充这些字段。
@Inject lateinit var logger: LoggerLocalDataSource
4.告诉Hilt从哪里去获取要自动依赖注入的实例
//将实例的作用域限定为 application 容器的注解是 @Singleton。该注解将使 application 容器始终提供相同的实例
@Singleton
class LoggerLocalDataSource @Inject constructor(private val logDao: LogDao) {...}
接口的依赖注入
假如 有接口LoggerDataSource并且LoggerLocalDataSource是实现它的
//将实例的作用域限定为 application 容器的注解是 @Singleton。该注解将使 application 容器始终提供相同的实例
@Singleton
class LoggerLocalDataSource @Inject constructor(private val logDao: LogDao):LoggerDataSource{...}
实现
@Inject lateinit var logger: LoggerDataSource
的依赖注入
新建一个类
@InstallIn(SingletonComponent::class)
@Module
abstract class LoggingDatabaseModule {
@Singleton
@Binds
abstract fun bindDatabaseLogger(impl: LoggerLocalDataSource): LoggerDataSource
}
给相同类型注入不同的实例
可能一个接口有多个实现类,可以使用标识符
@Qualifier
annotation class InMemoryLogger
@Qualifier
annotation class DatabaseLogger
//由于 LoggerDataSource 的不同实现的作用域限定为不同的容器,因此我们不能使用同一个模块
// LoggerInMemoryDataSource 的作用域限定为 Activity 容器,
// 而 LoggerLocalDataSource 的作用域限定为 Application 容器。
@InstallIn(SingletonComponent::class)
@Module
abstract class LoggingDatabaseModule {
@DatabaseLogger
@Singleton
@Binds
abstract fun bindDatabaseLogger(impl: LoggerLocalDataSource): LoggerDataSource
}
//@InstallIn(ActivityComponent::class)表明只能注入到Activity Fragment 和View中
@InstallIn(ActivityComponent::class)
@Module
abstract class LoggingInMemoryModule {
@InMemoryLogger
@ActivityScoped
@Binds
abstract fun bindInMemoryLogger(impl: LoggerInMemoryDataSource): LoggerDataSource
}
//要将某个类型的作用域限定为 Activity 容器,我们需要为该类型添加 @ActivityScoped 注解:
@ActivityScoped
class LoggerInMemoryDataSource @Inject constructor() : LoggerDataSource {...}
//将实例的作用域限定为 application 容器的注解是 @Singleton。该注解将使 application 容器始终提供相同的实例
@Singleton
class LoggerLocalDataSource @Inject constructor(private val logDao: LogDao):LoggerDataSource{...}
使用时:
//请注意,LoggerLocalDataSource 的实例与我们在 LogsFragment 中所用的实例相同,因为该类型的作用域限定为 application 容器。
// 但是,AppNavigator 的实例与 MainActivity 中的实例不同,因为我们尚未将其作用域限定为相应的 Activity 容器。
@InMemoryLogger
@Inject
lateinit var logger: LoggerDataSource
第三方类的依赖注入
告诉Hilt怎么去拿到这个第三方库类的实例即可
以Okhttp为例
@Module
@InstallIn(ActivityComponent::class)
class NetworkModule {
@Provides
fun provideOkHttpClient(): OkHttpClient {
return OkHttpClient.Builder()
.connectTimeout(20, TimeUnit.SECONDS)
.readTimeout(20, TimeUnit.SECONDS)
.writeTimeout(20, TimeUnit.SECONDS)
.build()
}
}
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject
lateinit var okHttpClient: OkHttpClient
...
}
Hilt内置组件和组件作用域
@InstallIn(ActivityComponent::class),就是把这个模块安装到 Activity 组件当中,另外,Activity 中包含的 Fragment 和 View 也可以使用.
如果想要在全程序范围内共用某个对象的实例,那么就使用 @Singleton。如果想要在某个 Activity,以及它内部包含的 Fragment 和 View 中共用某个对象的实例,那么就使用 @ActivityScoped。以此类推。
预置Qualifier
对于 Application 和 Activity 这两个类型,Hilt 给它们预置好了注入功能
class Driver @Inject constructor(val application: Application) {
}
class Driver @Inject constructor(val activity: Activity) {
}
这种写法编译将可以直接通过,无需添加任何注解声明。
ViewModel的依赖注入
对于 ViewModel 这种常用 Jetpack 组件,Hilt 专门为其提供了一种独立的依赖注入方式
在 app/build.gradle 文件中添加两个额外的依赖:
dependencies {
...
implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha02'
kapt 'androidx.hilt:hilt-compiler:1.0.0-alpha02'
}
class MyViewModel @ViewModelInject constructor(val repository: Repository) : ViewModel() {
...
}
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
val viewModel: MyViewModel by lazy { ViewModelProvider(this).get(MyViewModel::class.java) }
...
}
github仓库地址
OkAndGreat/android-hilt (github.com)
使用过程中可能会碰到的问题解决方案
Hilt升级到alpha03后报错 - 简书 (jianshu.com)
Ref: