作为 Jetpack 的一环,当然要尝试使用了~~~
注意:AS 版本要 4.0 以上,且必须开启 Java 8
使用 hilt 可以为类自动初始化,避免再编写 new XXXX
代码
Hilt 准备工作
根目录的 build.gradle
添加:
buildscript {
...
dependencies {
...
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.28-alpha'
}
}
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"
}
添加 Java 8
android {
...
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
当这一切做完后,出现了一个错误:
Gradle DSL method not found: 'kapt()'
还要再添加一个apply plugin,就像下面这样:
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
apply plugin: 'dagger.hilt.android.plugin'
使用 Hilt
使用 @HiltAndroidApp
注解标注 Application
@HiltAndroidApp
public class MApplication extends Application {
}
不要忘了在 清单文件中配置 Application ,然后要注解 Activity
@AndroidEntryPoint
public class MainActivity extends AppCompatActivity {
public static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
必须使用这两个注解之后才能运行
只有被标注的 Activity 才能使用注入功能
而且如果某个类要使用这个注解,则它的传入参数也必须使用了注解
定义两个类,注意这两个类的构造函数要有 @Inject
注解:
public class Me implements People {
Father father;
private String name;
private int age;
@Inject
public Me() {
Log.i(TAG, "me init");
}
}
public class Father implements People {
private String name;
private int age;
@Inject
public Father() {
Log.i(TAG, "father init");
}
}
在 Activity中声明对象 Me
要有 @Inject
注解:
@AndroidEntryPoint
public class MainActivity extends AppCompatActivity {
xxxxxxx
@Inject
Me me;
@Override
protected void onCreate(Bundle savedInstanceState) {
xxxxxxx
}
}
此时运行程序,即可在 Logcat
看到日志输出:
2020-09-17 16:19:37.587 8020-8020/com.zhou.myapplication I/MainActivity: me init
实际上,它是在运行过程中生成了一个代理类,然后再注入的,所以,Me 的构造函数,以及 Activity 中的对象 Me
可以看到都不是 private
修饰的,如果 private
修饰,则无法取到对象,从而运行失败
将其修改为 private
报错:
// Activity 修改对象访问权限
@Inject
private Me me;
// 报错
Dagger does not support injection into private fields
private Me me;
^
注解抽象类
上面只是注解普通的类,但是在实际使用过程中,可能在 Activity 中声明的是抽象类,至于对象具体是谁,则不关心,只要使用抽象类中的属性即可
在之前定义的 Me
和 People
中,他们都是实现的 People
接口
那么如果 Activity 中要接收的是一个 People
接口,Hilt
如果去注入呢
Hilt 支持使用 @Binds
注解绑定方法,这个方法的返回值要提供一个对象,绑定到 Activity 生命的对象上。尝试在 MainActivity 中定义一个方法:
@Inject
People me;
@Binds
People getPeople() {
Log.i(TAG, "get People Method");
return new Me();
}
然后在 MainActivity
中新增注解 @Module
@InstallIn(ActivityComponent.class)
,运行时报错:
@Binds methods must be abstract
People getPeople() {}
那么
@Module
和@InstallIn
是什么,它表明被注解的类是一个模块,且要被注入Activity
。如果被注入的是 Service 或者 Fragment 有对应的.class
。具体对应关系在最后
根据提示要求这个方法是一个抽象方法。那么把这个方法定义在一个抽象类里面吧:
@Module
@InstallIn(ActivityComponent.class)
public abstract class PeopleModule {
@Binds
abstract People getPeople(Me me);
}
再次运行就可以在控制台看到熟悉的输出了:
2020-09-18 11:51:44.920 5622-5622/com.zhou.myapplication I/MainActivity: me init
这种方式只能提供单一的类型,也就是 People 只能是 Me 或者是 Father,下列代码获取的两个对象都是 Me
,不要被命名迷惑了:
@Inject
People me;
@Inject
People father;
如果增加 Father 会怎么样呢,修改 PeopleModule ,在里面增加一个抽象方法
@Binds
abstract People getPeople(Me me);
@Binds
abstract People getPeople(Father me);
这时候运行会直接报错:
Cannot have more than one binding method with the same name in a single module
abstract People getPeople(Me me);
为同一对象提供多个绑定
上面一篇提到,同一个 People
只能用一个注解 @Binds
绑定一个 getPeople(Me me)
方法,当在同一个 Activity 里面,要有不同的对象实例怎么办?
这时候要用到注解 @Qualifier
和 @Retention(RetentionPolicy.RUNTIME)
新建一个类,在里面增加两个 interface
@Module
@InstallIn(ActivityComponent.class)
public class PeopleFactory {
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface ReturnMe {
}
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface ReturnFather {
}
}
首先定义了两个限定符,接下来要使用 @Provider
定义提供这两个类型的方法
@Provides
@ReturnMe
public static People providerMe() {
return new Me();
}
@Provides
@ReturnFather
public static People providerFather() {
return new Father();
}
定义这两个方法,使用 @Providers
表明这是一个类型提供者。然后使用限定符标志这是哪个对象的提供者
如何使用?
再回到 MainActivity
@PeopleFactory.ReturnMe
@Inject
People me;
@PeopleFactory.ReturnFather
@Inject
People father;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
使用 @PeopleFactory.ReturnMe
和 ReturnFather
就可以了。声明他们都是同一个接口类型 People
。但可以同时获得 Me
和 Father
两个对象。日志如下:
2020-09-21 15:21:25.266 8902-8902/com.zhou.myapplication I/MainActivity: me init
2020-09-21 15:21:25.266 8902-8902/com.zhou.myapplication I/MainActivity: father init
设置组件作用域
通过设置组件作用域可实现类似单例模式的功能,在 Application 中所有
@Inject
声明的对象都是同一个对象实例
@PeopleFactory.ReturnMe
@Inject
People me1;
@PeopleFactory.ReturnMe
@Inject
People me2;
通过这种方式,声明了两个 Me 对象,这个对象会创建两个,分别赋予 me1
和 me2
使用 @ActivityScoped
注解,可以确保在一个 Activity
作用域中只创建一次
@ActivityScoped
@Provides
@ReturnMe
public static People providerMe() {
return new Me();
}
现在看看日志输出:
if (me1 == me2) {
Log.i(TAG, "me1 == me2: true");
}
// Logcat 输出:
2020-09-21 16:48:46.438 3116-3116/com.zhou.myapplication I/MainActivity: me1 == me2: true
现在他们在 Activity 中是单例的。引用相同
如果想在整个 Application
中只创建一次,需要使用 @Singleton
且 class 使用
@InstallIn(ApplicationComponent .class)
注解,表明这个类注入到 Application 中
InstallIn 注入的 .class 对象表
Hilt 组件 | 注入器面向的对象 |
---|---|
ApplicationComponent | Application |
ActivityRetainedComponent | ViewModel |
ActivityComponent | Activity |
FragmentComponent | Fragment |
ViewComponent | View |
ViewWithFragmentComponent | 带有 @WithFragmentBindings 注释的 View |
ServiceComponent | Service |
设置作用域的注解表
Android 类 | 生成的组件 | 作用域 |
---|---|---|
Application | ApplicationComponent | @Singleton |
View Model | ActivityRetainedComponent | @ActivityRetainedScope |
Activity | ActivityComponent | @ActivityScoped |
Fragment | FragmentComponent | @FragmentScoped |
View | ViewComponent | @ViewScoped |
带有 @WithFragmentBindings 注释的 View | ViewWithFragmentComponent | @ViewScoped |
Service | ServiceComponent | @ServiceScoped |
后记
使用 Hilt
通过注入,可以接口数据对象。使其不需要编写样式化代码,数据在使用时,不关注 new
,而是使用即可。具体的数据提供通过 Module
提供。Hilt 在大型项目中使用可以解耦,带来便利性。
通过编写Demo程序,学习了 Hilt 的使用并了解其注解方式。就当扩充自己的知识库~~~
学无止境
over