文章目录
- 依赖注入
- Dagger
- Hilt
- Koin
- 添加依赖项
- Hilt常用注解的含义
- @HiltAndroidApp
- @AndroidEntryPoint
- @Inject
- @Module
- @InstallIn
- @Provides
- @EntryPoint
- Hilt组件生命周期和作用域
- 如何使用 Hilt 进行依赖注入
依赖注入
依赖注入是一种软件设计模式,它允许客户端从外部源获取其依赖项,而不是自己创建它们。它是一种用于在对象及其依赖项之间实现控制反转 (IoC) 的技术。
在安卓开发中,依赖注入(Dependency Injection,简称DI)是一种设计模式,用于解耦组件之间的依赖关系。它的基本思想是将对象的依赖关系从代码中移除,而是通过外部容器来管理和注入这些依赖。
传统的方式是在类内部直接创建和管理依赖对象,这样容易导致代码的耦合性增加,难以进行单元测试和代码重用。而依赖注入通过将依赖的创建和管理交给外部容器,使得类只关注自身的逻辑,而不需要关心依赖对象的创建和维护。
Dagger
Dagger是一个Java和Android平台上的依赖注入框架,由Google开发和维护。它的目标是简化管理对象之间的依赖关系,提高代码的可测试性、可扩展性和可维护性。
Dagger基于Java的注解处理器和反射机制,通过在编译时生成依赖注入代码,而不是在运行时进行反射。这种方式具有更好的性能和类型安全性,并且可以在编译时检测到一些错误,提供更好的开发体验。
Dagger是匕首的意思,依赖注入就好像是把匕首直接插入了需要注入的地方,直击要害。
Hilt
Hilt是刀把的意思,它把匕首最锋利的地方隐藏了起来,因为如果你用不好匕首的话反而可能会误伤自己。Hilt给你提供了一个安稳的把手,确保你可以安全简单地使用。
Android团队在Jetpack中增加了Hilt
,Hilt
是基于Dagger
的一个高级封装,旨在简化在Android应用中使用Dagger进行依赖注入的过程。Hilt提供了一套注解和工具,使得使用Dagger更加便捷和易于理解,并且提供了一些默认的配置,以减少开发者的配置工作量。并且得到了 Android Studio 的支持。
Hilt做的优化包括:
- 无需编写大量的Component代码
- Scope也会与Component自动绑定
- 预定义绑定,例如Application和Activity
- 预定义的限定符,例如
@ApplicationContext
和@ActivityContext
类似于Room等jetpack,利用注解功能简化了很多复杂的程序。
Hilt的核心思想仍然是依赖注入,它遵循了Dagger的注入原理和组件机制。Hilt的组件体系与Dagger类似,包括ApplicationComponent、ActivityComponent、FragmentComponent等,每个组件都有自己的生命周期和作用域。开发者可以使用Hilt的注解来标记和配置这些组件,以及在需要注入依赖的地方使用相应的注解
Koin
Koin - a smart Kotlin injection library to keep you focused on your app, not on your tools
Koin是为Kotlin开发者提供的一个实用型轻量级依赖注入框架,采用纯 Kotlin 语言编写而成,仅使用功能解析,无代理、无代码生成、无反射。
Hilt、Dagger、Koin等等都是依赖注入库,Google也在努力不断的完善依赖注入库从Dagger到Dagger2在到现在的Hilt,因为依赖注入是面向对象设计中最好的架构模式之一,使用依赖注入库有以下优点:
- 依赖注入库会自动释放不再使用的对象,减少资源的过度使用。
- 在配置scopes范围内,可重用依赖项和创建的实例,提高代码的可重用性,减少了很多模板代码。
- 代码变得更具可读性。
- 易于构建对象。
- 编写低耦合代码,更容易测试。
添加依赖项
首先,将 hilt-android-gradle-plugin
插件添加到项目的根级 build.gradle
文件中:
buildscript {
...
dependencies {
...
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.28-alpha'
}
}
然后,应用 Gradle 插件并在 app/build.gradle
文件中添加以下依赖项:
...
apply plugin: 'kotlin-kapt'
apply plugin: 'dagger.hilt.android.plugin'
android {
...
}
dependencies {
implementation "com.google.dagger:hilt-android:2.28-alpha"
kapt "com.google.dagger:hilt-android-compiler:2.28-alpha"
}
Hilt常用注解的含义
Hilt常用注解包含@HiltAndroidApp
、@AndroidEntryPoint
、@Inject
、@Module
、@InstallIn
、@Provides
、@EntryPoint
等等。
@HiltAndroidApp
- 所有使用Hilt的App必须包含一个使用@HiltAndroidApp注解的Application。它会替代Dagger中的AppComponent。
- @HiltAndroidApp注解将会触发Hilt代码的生成,作为应用程序依赖项容器的基类。
- 生成的Hilt组件依附于Application的生命周期,它也是App的父组件,提供其他组件访问的依赖。
- 在Application中设置好@HiltAndroidApp之后,就可以使用Hilt提供的组件了,组件包含Application、Activity、Fragment、View、Service、BroadcastReceiver 等等。
@AndroidEntryPoint
Hilt提供的@AndroidEntryPoint注解用于提供Android类的依赖(Activity、Fragment、View、Service、BroadcastReceiver)。
- Activity:仅仅支持ComponentActivity的子类例如FragmentActivity、AppCompatActivity等等。
- Fragment:仅仅支持继承androidx.Fragment的Fragment
- View
- Service
- BroadcastReceiver
如果您使用@AndroidEntryPoint为某个Android类添加注释,则还必须为依赖于该类的Android类添加注释。例如,如果您为某个Fragment添加注释,则还必须为使用该Fragment的所有Activity添加注释。
@Inject
Hilt需要知道如何从相应的组件中提供必要依赖的实例。使用@Inject注解来告诉Hilt如何提供该类的实例,它常用于构造函数、非私有字段、方法中。
注意:在构建时,Hilt为Android类生成Dagger组件。然后Dagger遍历您的代码并执行以下步骤:
- 构建并验证依赖关系,确保没有未满足的依赖关系。
- 生成它在运行时用于创建实际对象及其依赖项的类。
@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() {
@Inject lateinit var analytics: AnalyticsAdapter
...
}
注意:由Hilt注入的字段不能为私有字段。尝试使用Hilt注入私有字段会导致编译错误。
@Module
有时,类型不能通过构造函数注入。发生这种情况可能有多种原因。例如,您不能通过构造函数注入接口。此外,您也不能通过构造函数注入不归您所有的类型,如来自外部库的类。在这些情况下,您可以使用Hilt模块向Hilt提供绑定信息。
Hilt模块是一个带有@Module注释的类。与Dagger 模块一样,它会告知Hilt如何提供某些类型的实例。与Dagger 模块不同的是,您必须使用@InstallIn为Hilt模块添加注释,以告知Hilt每个模块将用在或安装在哪个Android类中。
常用于创建依赖类的对象(例如第三方库 OkHttp、Retrofit等等),使用@Module注解的类,需要使用@InstallIn注解指定module的范围。
@Module
@InstallIn(ApplicationComponent::class)
// 这里使用了 ApplicationComponent,因此 NetworkModule 绑定到 Application 的生命周期。
object NetworkModule {
}
@InstallIn
使用@Module注入的类,需要使用@InstallIn注解指定module的范围,例如使用 @InstallIn(ActivityComponent::class) 注解的module会绑定到activity的生命周期上。
Hilt提供了以下组件来绑定依赖与对应的Android类的活动范围。
Hilt 提供的组件 | 对应的 Android 类的活动范围 |
---|---|
ApplicationComponent | Application |
ActivityRetainedComponent | ViewModel |
ActivityComponent | Activity |
FragmentComponent | Fragment |
ViewComponent | View |
ViewWithFragmentComponent | View annotated with @WithFragmentBindings |
ServiceComponent | Service |
注意:Hilt没有为broadcast receivers提供组件,因为Hilt直接从ApplicationComponent注入broadcast receivers。
@Provides
它常用于被@Module注解标记类的内部的方法,并提供依赖项对象。
@Module
@InstallIn(ApplicationComponent::class)
// 这里使用了 ApplicationComponent,因此 NetworkModule 绑定到 Application 的生命周期。
object NetworkModule {
/**
* @Provides 常用于被 @Module 注解标记类的内部的方法,并提供依赖项对象。
* @Singleton 提供单例
*/
@Provides
@Singleton
fun provideOkHttpClient(): OkHttpClient {
return OkHttpClient.Builder()
.build()
}
}
@EntryPoint
Hilt支持最常见的Android类Application、Activity、Fragment、View、Service、BroadcastReceiver等等,但是您可能需要在Hilt不支持的类中执行依赖注入,在这种情况下可以使用@EntryPoint注解进行创建,Hilt会提供相应的依赖。
Hilt组件生命周期和作用域
如何使用 Hilt 进行依赖注入
我们先来看一个简单的例子,注入HiltSimple并在Application中调用它的方法。
class HiltSimple @Inject constructor() {
fun test1() {
Log.e(TAG, "已经创建成功 ")
}
}
@HiltAndroidApp
class HiltApplication : Application() {
@Inject
lateinit var mHiltSimple: HiltSimple
override fun onCreate() {
super.onCreate()
mHiltSimple.doSomething()
}
}
Hilt需要知道如何从相应的组件中提供必要依赖的实例。使用@Inject注解来告诉Hilt如何提供该类的实例,@Inject常用于构造函数、非私有字段、方法中。
- 可参考Jetpack新成员,一篇文章带你玩转Hilt和依赖注入