Hilt 目前支持注入的 Android 类共有如下 6 种:
Application(通过使用@HiltAndroidApp)ActivityFragmentViewServiceBroadcastReceiver
除 Application 需要 @HiltAndroidApp 修饰外,其他需要依赖注入的类都需要添加 @AndroidEntryPoint 注解。
@AndroidEntryPoint
class MainHiltActivity: AppCompatActivity() {
}
@Inject
接下来就可以通过 @Inject 来执行注入了:
@AndroidEntryPoint
class MainHiltActivity: AppCompatActivity() {
@Inject
lateinit var capBean: CapBean
}
到这里还并不能完成 capBean 字段的注入,这时 Hilt 并不知道如何创建实例对象。需要在构造函数上添加 @Inject 注解。这时 Hilt 就知道如何创建需要注入的实例对象了。
class CapBean @Inject constructor()
同理如果需要注入对象的构造函数中带有参数那么也需要对所需参数的构造函数添加 @Inject 注解。
class CapBean @Inject constructor(val waterBean: WaterBean)
class WaterBean @Inject constructor()
@Module
有些情况无法通过在构造函数中添加 @Inject 注解的方式来告知 Hilt 如何提供该类或接口的实例,比如:
- 接口。
- 来自外部库的类。
可以通过 @Module 注解来完成。@Module 注解会生成 Hilt 模块,在模块中提供具体的实现。
@Binds
注入接口实例,需要通过 @Module + @Binds 注解的方式。
首先定义一个接口:
interface Water {
fun drink()
}
然后定义接口的实现类:
class Milk @Inject constructor() : Water {
override fun drink() {
Log.e(“water”, “drink milk”)
}
}
然后定义一个抽象类添加 @Module 注解:
@Module
@InstallIn(ApplicationComponent::class)
abstract class WaterModule {
}
在抽象类中创建一个带 @Binds 注释的抽象函数:
@Module
@InstallIn(ApplicationComponent::class)
abstract class WaterModule {
@Binds
abstract fun bindsWater(milk: Milk): Water
}
带有 @Binds 注解的函数会向 Hilt 提供以下信息:
- 函数返回类型会告知
Hilt函数提供哪个接口的实例。 - 函数参数会告知
Hilt要提供哪种实现。
到这里 Hilt 就知道如何将接口的具体实现注入了。
@AndroidEntryPoint
class MainHiltActivity: AppCompatActivity() {
@Inject
lateinit var water: Water
}
@Provides
如果某个类不归你所有,也无法通过构造函数注入。可以通过 @Module + @Provides 注解告诉 Hilt 如何提供此类型的实例。
首先定义一个 @Module 注解修饰的普通类,这里是普通类而不是抽象类,因为我们要提供类实例的具体实现方式。以 OkHttpClient 为例:
@Module
@InstallIn(ActivityComponent::class)
class NetworkModule {
}
创建函数添加 @Provides 注解,函数体提供 OkHttpClient 对象实例的具体创建:
@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()
}
}
带有 @Provides 注解的函数会向 Hilt 提供以下信息:
- 函数返回类型会告知
Hilt函数提供哪个类型的实例。 - 函数参数会告知
Hilt相应类型的依赖项。 - 函数主体会告知
Hilt如何提供相应类型的实例。每当需要提供该类型的实例时,Hilt都会执行函数主体。
这时就可以通过 Hilt 注
入了:
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject
lateinit var okHttpClient: OkHttpClient
…
}
@Qualifier
在上面我们通过 @Binds 提供了 Water 接口的 Milk 实现,并成功注入到了类中。那如果在调用类中不仅需要 Milk 实现还需要另一个 Water 接口的实现 Juice。要怎么处理呢?
先添加 Juice 实现:
class Juice @Inject constructor() : Water {
override fun drink() {
Log.e(“water”, “drink juice”)
}
}
依然要添加到 @Module 模块中:
@Module
@InstallIn(ActivityComponent::class)
abstract class WaterModule {
@Binds
abstract fun bindsMilk(milk: Milk): Water
@Binds
abstract fun bindsJuice(juice: Juice): Water
}
到这里直接注入调用类中还不可以:
@AndroidEntryPoint
class MainHiltActivity: AppCompatActivity() {
@Inject
lateinit var juice: Water
@Inject
lateinit var milk: Water
}
Hilt 没有办法区分两个 Water 实例的不同的,这时需要 @Qualifier 来定义注解区分相同类型的不同实例了。
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class MilkWater
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class MilkWater
然后将定义的两个注解添加到 WaterModule 的函数中,以告诉 Hilt 如何提供同一实例的不同实例:
@Module
@InstallIn(ActivityComponent::class)
abstract class WaterModule {
@MilkWater
@Binds
abstract fun bindsMilk(milk: Milk): Water
@JuiceWater
@Binds
abstract fun bindsJuice(juice: Juice): Water
}
在调用类中也需要添加我们刚才定义的注解来区分两个实例的不同:
@AndroidEntryPoint
class MainHiltActivity: AppCompatActivity() {
@JuiceWater
@Inject
lateinit var juice: Water
@MilkWater
@Inject
lateinit var milk: Water
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main_hilt)
juice.drink()
milk.drink()
}
}
输出如下:
E/water: drink juice
E/water: drink milk
@ApplicationContext、@ActivityContext
这是 Hilt 提供了一些预定义的限定符。
@ApplicationContext:提供Application的Context。@ActivityContext:提供Activity的Context。
Hilt 组件
Hilt 组件的作用是将 Hilt 提供的实例注入到相应的 Android 类。Hilt 组件使用如下:
@Module
@InstallIn(ActivityComponent::class)
object MainModule {
@Provides
fun provideCoffeeBean(): CoffeeBean {
return CoffeeBean()
}
}
由于 @InstallIn 注解中设置的组件为 ActivityComponent,表示 Hilt 将通过 MainModule 为 Activity 提供的实例。
@Installin
@Installin 注解如下:
@Retention(CLASS)
@Target({ElementType.TYPE})
@GeneratesRootInput
public @interface InstallIn {
Class<?>[] value();
}
@Installin 包含一个字段,字段可用值为 Hilt 提供的组件,这些组件代表要注入的目标 Android 类。如下表:
| Hilt 组件 | 注入器面向的对象 |
|---|---|
| ApplicationComponent | Application |
| ActivityRetainedComponent | ViewModel |
| ActivityComponent | Activity |
| FragmentComponent | Fragment |
| ViewComponent | View |
| ViewWithFragmentComponent | 带有 @WithFragmentBindings 注释的 View |
| ServiceComponent | Service |
组件生命周期
Hilt 会按照相应 Android 类的生命周期自动创建和销毁生成的组件类的实例。
组件作用域
默认情况下,Hilt 中所有的实例都未设置限定作用域。也就是说,每次注入依赖时 Hilt 都会提供新的实例。如下:
class UserBean @Inject constructor()
@AndroidEntryPoint
class MainHiltActivity: AppCompatActivity() {
@Inject
lateinit var firstUserBean: UserBean
@Inject
lateinit var secondUserBean: UserBean
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main_hilt)
Log.e(“hilt”, firstUserBean.toString())
Log.e(“hilt”, secondUserBean.toString())
}
}
输出如下:
E/hilt: com.sample.hilt.UserBean@b01decb
E/hilt: com.sample.hilt.UserBean@dafe6a8
通过设置作用域注解,可以在相应 Android 类中共享同一实例。如下:
// 设置 UserBean 的作用域范围为 Activity 类
@ActivityScoped
onCreate(savedInstanceState)
setContentView(R.layout.activity_main_hilt)
Log.e(“hilt”, firstUserBean.toString())
Log.e(“hilt”, secondUserBean.toString())
}
}
输出如下:
E/hilt: com.sample.hilt.UserBean@b01decb
E/hilt: com.sample.hilt.UserBean@dafe6a8
通过设置作用域注解,可以在相应 Android 类中共享同一实例。如下:
// 设置 UserBean 的作用域范围为 Activity 类
@ActivityScoped









