Android Hilt的使用以及遇到的问题
目录
- 简介
- 导入Hilt
- 组件层次
- 组件默认绑定
- 简单使用
- @HiltAndroidApp
- 介绍
- 使用
- @AndroidEntryPoint
- 介绍
- 使用
- @Module 和 @InstallIn
- 介绍
- 使用
- @Provides 和 @Binds
- 介绍
- 使用
- @HiltViewModel
- 介绍
- 使用
- @EntryPoint
- 介绍
- 小结
简介
Hilt 提供了一种将Dagger 依赖注入到Android 应用程序的标准方法。为Android 应用程序简化提供一组标准的、简化设置、可以读的组件;且为不同类型的构建(例如:测试、调试、发行)提供一种简单的方法。
可以理解为Google 为了统一依赖注入组件,但是Dagger 用起来比较复杂。就针对Android开发了一套适配库。
导入Hilt
apply plugin: 'com.android.application' apply plugin: 'dagger.hilt.android.plugin' android { // ... } dependencies { implementation 'com.google.dagger:hilt-android:2.34.1-beta' kapt 'com.google.dagger:hilt-compiler:2.34.1-beta' // For instrumentation tests androidTestImplementation 'com.google.dagger:hilt-android-testing:2.34.1-beta' kaptAndroidTest 'com.google.dagger:hilt-compiler:2.34.1-beta' // For local unit tests testImplementation 'com.google.dagger:hilt-android-testing:2.34.1-beta' kaptTest 'com.google.dagger:hilt-compiler:2.34.1-beta' } kapt { correctErrorTypes true }
设置correctErrorTypes 为true ,将kapt配置为更正错误类型 。
这里遇到一个问题,当我的gradle 版本为 3.4.1 的时候
classpath 'com.android.tools.build:gradle:3.4.1'
apply plugin: 'dagger.hilt.android.plugin'插件一直安装失败,
提示找不到 "com/android/Version" 把gradle 改成 4.1.2 就没问题了
且注意 如果你是多module的项目,
apply plugin: 'dagger.hilt.android.plugin' 一定要plugin在主module下
(也就是跟 apply plugin: 'com.android.application' 一起),
若是只在子module下,主module的注入不会被实现。(问题1,后面会解释问题原因)
buildscript { repositories { // other repositories... mavenCentral() } dependencies { // other plugins... classpath 'com.google.dagger:hilt-android-gradle-plugin:2.34.1-beta' } }
组件层次
Hilt把Dagger 手动创建Component 改成了预定义的Component,且自动集成到Android应用程序的各个生命周期中。通过注解的方式@InstallIn(xxxComponent.class)进行绑定。
下图显示了标准的Hilt组件层次结构。每个组件上方的注释是作用域注释,用于将绑定范围限制为该组件的生存期。组件下方的箭头指向任何子组件。通常,子组件中的绑定可以依赖于祖先组件中的任何绑定。
组件默认绑定
每个Hilt 组件都带有一组默认绑定,这些默认绑定可以作为依赖注入到你自定义绑定中
Component | Default Bindings |
---|---|
SingletonComponent | Application |
ActivityRetainedComponent | Application |
ViewModelComponent | SavedStateHandle |
ActivityComponent | Application, Acitvity |
FragmentComponent | Application, Acitvity, Fragment |
ViewComponent | Application, Acitvity, View |
ViewWithFragmentComponent | Application, Acitvity, Fragment, View |
ServiceComponent | Application, Service |
简单使用
下面我为大家介绍以下一些注解的使用:
- @HiltAndroidApp
- @AndroidEntryPoint
- @InstallIn
- @Module
- @Provides
- @Binds
- @HiltViewModel
- @EntryPoint
想要了解更多的建议直接查看官方文档
@HiltAndroidApp
介绍
所有使用Hilt的App 必须包含一个被@HiltAndroidApp 注释的Appliction 类。
@HiltAndroidApp 会生成一个Hilt_MyApplication 的基类,并且继承与@HiltAndroidApp 注释的类的基类,然后将@HiltAndroidApp 注释的类的基类替换成Hilt_MyApplication。例如:
这是我们应用的 MyApplication
@HiltAndroidApp class MyApplication extends BaseApplication{ }
使用@HiltAndroidApp Hilt 将会生成 Hilt_MyApplication
public abstract class Hilt_MyApplication extends BaseApplication implements GeneratedComponentManagerHolder { private final ApplicationComponentManager componentManager = new ApplicationComponentManager(new ComponentSupplier() { @Override public Object get() { 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() { // This is a known unsafe cast, but is safe in the only correct use case: // MyApplication extends Hilt_MyApplication ((MyApplication_GeneratedInjector) generatedComponent()).injectMyApplication(UnsafeCasts.<MyApplication>unsafeCast(this)); super.onCreate(); } }
并且使我们的 MyApplication 继承 Hilt_MyApplication,通过这种方式将modules 注入到我们的应用中。
可以看到具体的注入方法就是Hilt_MyApplication onCreate() 函数中的
((MyApplication_GeneratedInjector) generatedComponent()).injectMyApplication(UnsafeCasts.unsafeCast(this));
这句代码,generatedComponent() 返回的是 MyApplication_HiltComponents.SingletonC 对象,这个对象中就是我们所有module 的代码实现。有兴趣的同学可以自己去看一下,我这里就不贴代码了
使用
使用分为两种情况,添加和没有添加 Hilt Gradle插件
//没有添加插件 @HiltAndroidApp(BaseApplication.class) class MyApplication extends Hilt_MyApplication{} //添加插件 @HiltAndroidApp class MyApplication extends BaseApplication{}
建议添加插件,使用起来会更简单。本文以下的示例都假定以使用插件。
这里需要注意的是如果要在MyApplication 中使用注入的对象,需要在 super.onCreate() 之后才能使用。 原因且看介绍中的 Hilt_MyApplication 源码。
这里解释一下问题1出现的原因,是因为我没有添加插件但@HiltAndroidApp 使用的时候用的却是添加了插件的用法。所以会出现module 注入不被实现的情况。
@AndroidEntryPoint
介绍
安卓成员注入,使用@AndroidEntryPoint 注解后就可以在该类中使用module注入的成员变量。但@AndroidEntryPoint 有类型限制,只能在以下的类上使用:
- Activity
- Fragment
- View
- Service
- BroadcastReceiver
使用
@AndroidEntryPoint public final class MyActivity extends MyBaseActivity { // Bindings in SingletonComponent or ActivityComponent @Inject Bar bar; @Override public void onCreate(Bundle savedInstanceState) { // Injection happens in super.onCreate(). super.onCreate(); // Do something with bar ... } }
同样要注意是是需要在 super.onCreate() 后使用注入的成员变量
@Module 和 @InstallIn
介绍
@Module 跟Dagger 里的是同一个,没什么好说的。
@InstallIn 通过使用@InstallIn(xxxComponent.class) 将module 安装到指定的组件中,在Hilt 中所以module 都必须添加这个注释,如果组件中就找不到这个module ,可能引起编译错误。
当然一个module 也可安装到多个组件上如:@InstallIn({ViewComponent.class, ViewWithFragmentComponent.class})
使用
@Module @InstallIn(SingletonComponent.class) public final class FooModule { // @InstallIn(SingletonComponent.class) module providers have access to // the Application binding. @Provides static Bar provideBar(Application app) {...} }
每个组件都带有作用域注释,该注释可用于记住对组件生存期的绑定。例如,要将范围绑定到 SingletonComponent组件,请使用@Singleton批注:
@Module @InstallIn(SingletonComponent.class) public final class FooModule { // @Singleton providers are only called once per SingletonComponent instance. @Provides @Singleton static Bar provideBar() {...} }
此外,每个组件都有默认情况下可用的绑定。例如,该SingletonComponent组件提供了Application 绑定:
@Module @InstallIn(SingletonComponent.class) public final class FooModule { // @InstallIn(SingletonComponent.class) module providers have access to // the Application binding. @Provides static Bar provideBar(Application app) {...} }
@Provides 和 @Binds
介绍
@Provides 注释Module 中的方法以创建提供者方法绑定。该方法的返回类型绑定到其返回值。
@Binds 注释Module 中的抽象方法,一般方法的返回是一个接口,参数是实现接口的子类,在调用是会调用参数的子类中的方法实现。
使用
@Module @InstallIn(SingletonComponent.class) public final class FooModule { @Provides @Singleton static Bar provideBar() {...} } @Module @InstallIn(SingletonComponent.class) public abstract class BindModule { @Binds @Singleton abstract Random bindRandom(SecureRandom secureRandom); }
@HiltViewModel
介绍
使用 @HiltViewModel 注释ViewModel,ViewModel 在创建的时候就会走Hilt 创建的HiltViewModelFactory 进行创建。就可以使用在创建的时候使用Module 中提供的实例
使用
@HiltViewModel public final class FooViewModel extends ViewModel { @Inject FooViewModel(SavedStateHandle handle, Foo foo) { // ... } }
然后就可以在带有@AndroidEntryPoint 注解的activity、fragment 中使用了
@AndroidEntryPoint public final class MyActivity extends AppCompatActivity { private FooViewModel fooViewModel; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); fooViewModel = new ViewModelProvider(this).get(FooViewModel.class); } }
@EntryPoint
介绍
为不能使用注解的地方提供注入的对象。因为@AndroidEntryPoint 使用范围有限,在这范围之外要使用Hilt 注入的实例就可以使用@EntryPoint 来实现。
这个像是Hilt 把Component标准化后,使用者不能再里面添加方法,导致不能为使用不了注解的地方提供依赖而做出的解决方案。
@EntryPoint @InstallIn(SingletonComponent.class) public interface FooBarInterface { Bar getBar(); }
如果使用上面的定义
Bar bar = EntryPoints.get(applicationContext, FooBarInterface.class).getBar();
小结
一开始使用的时候我看到是 安卓开发平台“Hilt 和 Jetpack 集成”这个文档,真坑,文档不及时更新也不把官方链接放一下。吐槽一下。然后几经周转找到了官方文档才能有幸为大家介绍一下Hilt。
使用起来确实要比Dagger 舒服的多,少了很多模板代码,范围和生命周期的绑定也更好理解。不多bb 学它
以上就是Android Hilt的使用以及遇到的问题的详细内容,更多关于Android Hilt的资料请关注我们其它相关文章!