Hilt是Google基于Dagger2开发的专用于Android的依赖注入框架,相比Dagger,它的优势如下:
- 简化了Android应用中Dagger相基础架构。
- 创建了一组标准的组件和作用域,以简化设置、提高可读性以及在应用之间共享代码。
- 提供了一种简单的方法来为各种构建类型(如测试、调试或发布)配置不同的绑定。
引入依赖
-
在项目根级
build.gradle中添加hilt-android-gradle-plugin插件。buildscript { dependencies { classpath 'com.google.dagger:hilt-android-gradle-plugin:x.y.z' } } -
在模块级
build.gradle中应用插件和导入依赖。plugins { id 'kotlin-kapt' id 'dagger.hilt.android.plugin' } android { ... // hilt需要开启Java8 compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } } dependencies { implementation "com.google.dagger:hilt-android:2.38.1" kapt "com.google.dagger:hilt-compiler:2.38.1" }
使用
Hilt是在Dagger2的基础上进行开发的,其本质还是对Dagger的使用进行简化。相比dagger- android,Hilt会帮助我们自动生成更多的模板代码,从而让使用过程更加简洁。但无论怎么精简,其Dagger的底层实现还是不变的,因此,了解Dagger将帮助我们更好的掌握Hilt,关于Dagger的介绍参见Dagger 系列文章。
定义应用类
使用Hilt需要为应用的Application类添加@HiltAndroidApp注解,使用了此注解后,Hilt将会使用gradle plugin为MyApplication生成一个名为Hilt_Application的父类(gradle插件会自动调整MyApplication的继承)以及应用级别的Component。
@HiltAndroidApp
class MyApplication : Application() {
...
}
在Hilt_Application中包含着对应用级别Component的初始化代码以及对Application的注入代码:
public abstract class Hilt_MyApplication
extends Application
implements GeneratedComponentManagerHolder
{
private final ApplicationComponentManager componentManager =
new ApplicationComponentManager(new ComponentSupplier() {
@Override
public Object get() {
// 应用级别的Component的初始化
return DaggerMyApplication_HiltComponents_SingletonC.builder()
.applicationContextModule(new ApplicationContextModule(Hilt_MyApplication.this))
.build();
}
});
@Override
public final ApplicationComponentManager componentManager() {
return componentManager;
}
@Override
public final Object generatedComponent() {
return this.componentManager().generatedComponent();
}
@CallSuper
@Override
public void onCreate() {
// 注入
((MyApplication_GeneratedInjector) generatedComponent()).injectMyApplication(UnsafeCasts.<MyApplication>unsafeCast(this));
super.onCreate();
}
}
关于DaggerMyApplication_HiltComponents_SingletonC的内容将在后文分析。
为Android其它组件注入依赖
Hilt支持为以下Android组件注入依赖:
ApplicationViewModelComponentActivity(不支持基础的Activity)androidx.Fragment(不支持基础的Fragment)ViewServiceBroadcastReceiver
要为Android组件注入依赖,需要使用@AndroidEntryPoint注解标记对应的组件类(对于Application类需要使用@HiltAndroidApp,对于ViewModel需要使用@HiltViewModel),获取依赖项则与Dagger一样,使用@Inject注解标记非私有字段即可。
@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() {
@Inject lateinit var analytics: AnalyticsAdapter
...
}
与@HiltAndroidApp一样,Hilt也会为@AndroidEntryPoint和@HiltViewModel标记的组件生成相应的Component和用于实现注入的父类。
绑定(依赖提供)
Hilt中的绑定与Dagger中的定义方式一致。
对于我们可以控制的依赖类型,直接在构造函数上使用@Inject注解即可。
class Student @Inject constructor() {
...
}
对于无法使用constructor-inject的依赖类型,也可以使用模块提供绑定信息。Hilt的模块和Dagger的模块是一样的,也是使用@Module标记的类。但需要注意的是,在Hilt中需要使用@InstallIn为模块指定要安装到的Component,关于Component的介绍见后文组件 Component 。
@Module
@InstallIn(SingletonComponent::class)
class CommonModule {
...
}
和Dagger一样,在模块中,我们可以使用@Provides以及@Binds系列的注解来绑定依赖项。
@Module
@InstallIn(SingletonComponent::class)
class CommonModule {
@Provides
fun provideSp(
context: Application
): SharedPreferences {
return context.getSharedPreferences("main", Context.MODE_PRIVATE)
}
@Binds
abstract fun bindAnalyticsService(
analyticsServiceImpl: AnalyticsServiceImpl
): AnalyticsService
...
}
限定符 Qualifier
Hilt中限定符的使用也与Dagger中一样。但是Hilt为Android提供了两个预定义的限定符:@ApplicationContext与@ActivityContext,用来限定注入的context是应用的context还是Activity的context。
组件 Component
使用Hilt时,大部分时候我们都无需手动定义Component,Hilt会自动的帮我们生成与Android组件相应的Component。Hilt提供了以下组件的自动生成:
| Android 组件 | Hilt Component |
|---|---|
| Application | SingletonComponent |
| - | ActivityRetainedComponent |
| ViewModel | ViewModelComponent |
| Activity | ActivityComponent |
| Fragment | FragmentComponent |
| View | ViewComponent |
| 使用@WithFragmentBindings标记的View | ViewWithFragmentComponent |
| Service | ServiceComponent |
💡 Hilt不会为
BroadcastReceiver生成组件,BroadcastReceiver直接通过SingletonComponent执行注入。
💡 Hilt也允许你自定义Component,但是大部分时候都不需要它,关于自定义Component可以参考官方文档。
组件的层次结构
Hilt会将生成的Component组织成一定的层次结构,在这个层次结构中,位于下面的组件可以直接使用上层的子组件的依赖项,比如ViewComponent可以使用ActivityComponent中的依赖项;ActivityComponent可以使用SingletonComponent中的依赖项。
组件的生命周期
Hilt会按照对应Android组件类的生命周期自动创建和销毁生成Component实例。
| Component | 创建时机 | 销毁时机 |
|---|---|---|
| SingletonComponent | Application#onCreate() | Application#已销毁 |
| ActivityRetainedComponent | Activity#onCreate() | Activity#onDestroy() |
| ViewModelComponent | ViewModel已创建 | ViewModel已销毁 |
| ActivityComponent | Activity#onCreate() | Activity#onDestroy() |
| FragmentComponent | Fragment#attach() | Fragment#onDestroy() |
| ViewComponent | View#super() | View已销毁 |
| ViewWithFragmentComponent | View#super() | View已销毁 |
| ServiceComponent | Service#onCreate() | Service#onDestroy() |
💡 相比
ActivityComponent,ActivityRetainedComponent在应用配置更改(如横竖屏)引起Activity重建后依然存在。
❓ Q:
ActivityRetainedComponent与ActivityComponent创建和销毁时机一致,为何ActivityRetainedComponent可以在配置更改后仍然存在,ActivityComponent则不行?
💡 A:ActivityRetainedComponentManager会创建ActivityRetainedComponentViewModel,然后把ActivityRetainedComponent的实例存储在其中,ActivityRetainedComponentViewModel实例则通过ViewModelStore保存到Activity中。换句话说,其实就是借用了ViewModel的重建保存能力。
Component的默认绑定
Hilt会为生成的Component提供一些默认绑定,比如对于SingletonComponent,Hilt会为其默认绑定Application实例,对于ActivityComponent,Hilt则会为其默认绑定对应Activity的实例。除此之外,因为Hilt中Component的层次关系,Component也可以访问到SubComponent提供的默认绑定。比如ActivityComponent也可以访问到SingletonComponent中的Application实例。
下表给出了各种Component可以访问到的默认绑定依赖项:
| Component | 默认绑定依赖项 |
|---|---|
| SingletonComponent | Application |
| ActivityRetainedComponent | Application |
| ViewModelComponent | SavedStateHandle |
| ActivityComponent | Application、Activity |
| FragmentComponent | Application、Activity、Fragment |
| ViewComponent | Application、Activity、View |
| ViewWithFragmentComponent | Application、Activity、Fragment、View |
| ServiceComponent | Application、Service |
作用域
Hilt中作用域的使用也与Dagger中一样。同样的Hilt也为生成的Component提供了预置的作用域注解,用于将依赖项实例的生命周期限制在对应Component的生命周期中:
| Component | 作用域 |
|---|---|
| SingletonComponent | @Singleton |
| ActivityRetainedComponent | @ActivityRetainedScoped |
| ViewModelComponent | @ViewModelScoped |
| ActivityComponent | @ActivityScoped |
| FragmentComponent | @FragmentScoped |
| ViewComponent | @ViewScoped |
| ViewWithFragmentComponent | @ViewScoped |
| ServiceComponent | @ServiceScoped |
在Hilt不支持的类中实现注入
Dagger中,我们可以在Component中编写返回依赖项实例的接口来对外暴露依赖图中的依赖项。
@Component(modules = [CommonModule::class])
interface AppComponent {
fun inject(application: Application)
fun exposeSharedPreference(): SharedPreference
}
class MyClassCannotBeInject {
fun test(component: AppComponent) {
val sp = component.exposeSharedPreference()
...
}
}
但在Hilt中因为Component是自动生成的,我们无法直接为其编写暴露依赖项的接口。为此Hilt提供了@EntryPoint注解,@EntryPoint可以定义一个入口点,其它Hilt不支持管理的类可以通过入口点访问依赖图中的依赖项。@EntryPoint需要配合@InstallIn一起使用,表明要附加到哪个Component上。
@EntryPoint
@InstallIn(SingletonComponent::class)
interface ExampleEntryPoint {
fun sharedPreference(): SharedPreference
}
定义了入口点后,我们就可以使用EntryPointAccessors中适当的静态方法获取访问点。比如我们的访问点安装在SingletonComponent上,就需要使用fromApplication方法。
EntryPointAccessors.fromApplication(appContext,
ExampleEntryPoint::class.java)
val sp = hiltEntryPoint.sharedPreference()
实现原理
Hilt的访问点在实现上实际上是在生成Component时,将@EntryPoint标记的接口作为其父接口继承其中暴露的接口方法。
@Component(
modules = {
ApplicationContextModule.class,
CommonModule.class,
ActivityRetainedCBuilderModule.class,
ServiceCBuilderModule.class
}
)
@Singleton
public abstract static class SingletonC implements
HiltWrapper_ActivityRetainedComponentManager_ActivityRetainedComponentBuilderEntryPoint,
ServiceComponentManager.ServiceComponentBuilderEntryPoint,
SingletonComponent,
GeneratedComponent,
MyApplication_GeneratedInjector,
ExampleEntryPoint { }
public final class DaggerMyApplication_HiltComponents_SingletonC
extends MyApplication_HiltComponents.SingletonC {
...
@Override
public SharedPreference sharedPreference() {
return provideSharedPreferenceProvider.get();
}
}
资料
-
Google官方使用指引