Dagger是一个很古老的框架了,当初诞生时候,主要是为了模块之间的解耦。本篇文章主要介绍一下如何使用dagger2,后续会介绍其原理。
AS集成
对于现在的AS项目,一般都是会集成Kotlin和Java混写,所以可以在想要使用dagger的模块module的gradle下加入如下配置。
java
implementation 'com.google.dagger:dagger:2.21'
kapt 'com.google.dagger:dagger-compiler:2.21'
注意这里用到了kapt,这就要求我们对应module的gradle顶部有如下配置:
arduino
apply plugin: 'kotlin-kapt'
如何使用
假设我们现在存在一个类TestA, 结构如下:
csharp
public class TestA {
private int count = 0;
public String getValue() {
return "123321";
}
public int addSelf(){
return ++count;
}
}
我们现在呢,要在TestB中使用到TestA的对象实例,常规写法呢,就是new一个对象进行方法调用:
- 常规写法
csharp
public class TestB {
private final TestA testA = new TestA();
public void show() {
for (int i = 0; i < 2; i++) {
testA.addSelf();
System.out.println(testA.getValue());
}
}
}
- 使用dagger
常规写法要求我们对于一个对象必须要实例化,才能使用。那么在项目比较大型的时候,如果很多地方都用到了该对象,且进行了实例化。那么后续只要对象构造器里增加一个入参,那么就需要修改所有地方。
这样很容易出错,dagger这个时候就可以起作用了,dagger可以帮我们自动去书写实例化对象的代码,而我们只需要关注使用testA对象即可。
让我们看下使用dagger,这里代码怎么写。
csharp
public class TestB {
@Inject
TestA testA;
public void show() {
for (int i = 0; i < 2; i++) {
testA.addSelf();
System.out.println(testA.getValue());
}
}
}
可以看到,区别就是将new TestA去掉了,加上了@Inject注解,让Dagger自动帮我们去进行TestA的实例化,我们只需要加上注解后,在下方直接使用其方法即可。
现在我们知道了dagger的作用和简单的使用场景,我们来看下如果要实现上述例子中TestA这种直接加注解来实例对象的方式,该怎么操作。
使用dagger来实现TestA自动实例化
下图是Dagger里最基本的几个注解的使用及关系。
对于上述TestA的例子。我们首先按照上图所示,定义一个Module,如下:
typescript
@Module
public class TestAModule {
@Provides
public TestA provide() {
return new TestA();
}
}
这里对于provide方法可以任意命名,只要返回值是TestA就可以。同时TestAModule也可以任意命名,只要加上Module注解即可。
接着,我们定义桥梁Component,类和方法也可以随意命名。
java
@Component(modules = TestAModule.class)
public interface TestAComponent {
void injectIntoTestB(TestB testB);
}
注意Component上要写上,该Component所对应的Module,以便Dagger可以关联两者。同时内部定义一个接口也需要传入TestB对象,以便告诉Dagger,我们想要在哪个类中使用TestA实例。
做完这些,最后一步就是在TestB中使用了。
csharp
public class TestB {
@Inject
TestA testA;
public void show() {
for (int i = 0; i < 2; i++) {
testA.addSelf();
System.out.println(testA.getValue());
}
}
}
注意这里TestA不可以是private修饰。
我们需要先build一下,编译完成后,还需要最后一步,在TestB中运行一下Dagger。可以在show方法中加上如下方法:
kotlin
DaggerTestAComponent.create().injectIntoTestB(this);
DaggerTestAComponent是Dagger在我们构建编译时候为我们生成的,所以此时不编译是看不到这个类的,必须编译一下。
运行一下,我们就可以使用这个类了。所有Dagger自动生成的类都是以Dagger开头。
<math xmlns="http://www.w3.org/1998/Math/MathML"> 如果找不到文件!! \color{red}{如果找不到文件!!} </math>如果找不到文件!!
可能某些小伙伴发现,编译后仍然找不到这个类,那么说明你的项目开启了增量编译,需要gradle.properties中将其关闭。 在 gradle.properties 中加上如下配置,然后删除build文件夹,重新编译。
ini
kapt.incremental.apt = false
最后我们运行一下,发现日志可以成功打印,说明执行成功了。
注意这个时候,TestB文件中张这样。
csharp
public class TestB {
@Inject
TestA testA;
public void show() {
DaggerTestAComponent.create().injectIntoTestB(this);
for (int i = 0; i < 2; i++) {
testA.addSelf();
System.out.println(testA.getValue());
}
}
实现单例
上述我们简单实现了个例子,同时我们可以在类中直接实例化一个对象并进行使用。我们看下如何实现。
我们可以在Provides注解标注的方法上加上Singleton注解,表示通过该方法生成的对象为单例对象,但是要注意,该单例的范围仅限于inject时候传入的对象。
比如:
less
@Module
public class TestAModule {
@Provides
@Singleton
public TestA provide() {
return new TestA();
}
}
注意在Component上也要加上Singleton注解。
less
@Component(modules = TestAModule.class)
@Singleton
public interface TestAComponent {
void injectIntoTestB(TestB testB);
void injectIntoTestC(TestC testC);
}
对于上述方法,我在TestB和TestC中使用时候:
kotlin
public class TestB {
@Inject
TestA testA1;
@Inject
TestA testA2;
public void show() {
DaggerTestAComponent.create().injectIntoTestB(this);
System.out.println(testA1);
System.out.println(testA2);
}
}
public class TestC {
@Inject
TestA testA1;
@Inject
TestA testA2;
public void dd() {
DaggerTestAComponent.create().injectIntoTestC(this);
System.out.println("C:" + testA1);
System.out.println("C:" + testA2);
}
}
我们同时运行dd和show方法时候,会发现两个dd方法内地址打印一样,show方法内地址打印一样,但是dd方法和show方法打印的地址就不一样了。这是因为单例的范围和inject传入的对象有关,只在传入的对象范围内生效。
name标签
Dagger存在一个Name标签,主要用于确定对象的不同生成方式。比如我们TestA存在多个构造器。
csharp
public class TestA {
public TestA() {
}
public TestA(int count) {
this.count = count;
}
private int count = 0;
public String getValue() {
return "123321 " + count;
}
那么可以在module中使用name来区分不同方式来生成对象实例。
typescript
@Module
public class TestAModule {
@Provides
@Named("11")
public TestA provide() {
return new TestA();
}
@Provides
@Named("22")
public TestA provide2() {
return new TestA(22);
}
我们在TestB中使用,
less
public class TestB {
@Inject
@Named("11")
TestA testA1;
@Inject
@Named("22")
TestA testA2;
public void show() {
DaggerTestAComponent.create().injectIntoTestB(this);
System.out.println("11=>" + testA1.getValue());
System.out.println("22=>" + testA2.getValue());
}
运行一下方法 show,可以看到此时testA1和testA2分别调用了不同的构造器生成了对象。
总结
个人感觉,dagger在当下直接使用,比较鸡肋,想不到很合适的使用场景,毕竟当下这么多框架和新技术,对于各种场景,完全有更合适的框架。所以本文只打算对dagger研究这么多。为后续Hilt学习打个基础吧。