Dagger2 入门系列一:基础使用,从基础到源码统统帮你搞定
buildTypes
{
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation 'com.google.dagger:dagger:2.17'
annotationProcessor 'com.google.dagger:dagger-compiler:2.17'
// implementation 'com.google.dagger:dagger-android:2.17'
// implementation 'com.google.dagger:dagger-android-support:2.17'
// annotationProcessor 'com.google.dagger:dagger-android-processor:2.17'
//butterknife
implementation 'com.jakewharton:butterknife:10.2.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.1'
}
2、代码实战
======
2.1、创建一个 Student 类,并添加 Inject 注解
import javax.inject.Inject;
public class Student {
@Inject
public Student() {
}
}
备注 1:添加 @Inject 后,使用 Ctrl+F9(或者 rebuild)进行一次编译。
备注 2:javax.inject 包的目录如下所示:
![](https://static001.geekbang.org/infoq/95/958e165019cef1e7c110497e671e2140.png)
2.2、编译后项目的变化
在目录 Dagger2Test1\app\build\generated\ap_generated_sources\debug\out\com\gs\dagtest1\bean 可以看到一个 Student_Factory 的类。
![](https://static001.geekbang.org/infoq/4b/4b49c415e4a944f545ad6d558189b2a7.png)
具体内容:
// Generated by Dagger (https://google.github.io/dagger).
package com.gs.dagtest1.bean;
import dagger.internal.Factory;
public final class Student_Factory implements Factory<Student> {
private static final Student_Factory INSTANCE = new Student_Factory();
@Override
public Student get() {
return provideInstance();
}
public static Student provideInstance() {
return new Student();
}
public static Student_Factory create() {
return INSTANCE;
}
public static Student newStudent() {
return new Student();
}
}
可以看到这是一个工厂类,而我们可以通过三种方式获取到 Student 对象,如下:
Student student1 = Student_Factory.create().get();
LogUtils.e(student1.toString());
Student student2 = Student_Factory.newStudent();
LogUtils.e(student2.toString());
Student student3 = Student_Factory.provideInstance();
LogUtils.e(student3.toString());
打印结果如下:
![](https://static001.geekbang.org/infoq/7c/7cf9f131a025ab9a43a0728d9154542f.png)
综上,我们通过 @Inject 注解了一个 Student 的构造方法后,可以让编译器帮助我们生成一个对应的工厂类 Student_Factory,通过该工厂类,我们可以通过三种方法获取到 Student 对象。
当然,通过这三种方式获取到 Student 对象我们自己就可以实现,这里只是让大家更方便地理解编译类。
2.3、获取 Student 对象
我们创建一个 Activity 来调用 Student
public class Test1Activity extends AppCompatActivity {
@Inject
Student student;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test1);
ButterKnife.bind(this);
}
@OnClick(R.id.btn1)
public void onViewClicked() {
Toast.makeText(this, student.toString(), Toast.LENGTH_SHORT).show();
}
}
接下来我们创建一个 Activity 类,在这个类中创建一个成员变量 Student,按照 Dagger2 给我们的指示,当我们需要一个 Student,我们只需要在这个成员变量上方加一个 @Inject 注解,编译器会自动帮我们产生对应的代码,我们就可以直接使用这个 Student 对象了!
本案例中我们设置一个 Button,点击 Button 后我们打印出这个 Student 对象。
事实真的如此吗?我们直接运行代码,并点击 Button,很遗憾,直接报空指针异常:
![](https://static001.geekbang.org/infoq/62/629f1500a6a702ebd3af5975e387806a.png)
显然,和平常使用的结果一样,@Inject 并没有帮助我们初始化对应的 Student 对象,或者说,我们的 Activity 并没有使用刚才我们看到的 Student_Factory 类,不过也可以理解,我们并没有建立 Activity 和 Student_Factory 类之间的关系嘛。
2.4、创建 Module 类和 Component 接口
import dagger.Module;
@Module
public class Test1Module {
private Test1Activity activity;
Test1Module(Test1Activity activity) {
this.activity = activity;
}
}
import dagger.Component;
@Component(modules = Test1Module.class)
public interface Test1Component {
void inject(Test1Activity activity);
}
请注意,Module 类上方的 @Module 注解意味着这是一个提供数据的【模块】,而 Component 接口上方的 @Component(modules = Test1Module.class)说明这是一个【组件】(我更喜欢称呼它为注射器)。
突然出现的这两个类可以称得上是莫名其妙,因为我们从代码上来看并不知道这对于 Student 和 Activity 之间关系有什么实质性的进展,但假如我们这时在 Activty 中添加这一段代码:
public class Test1Activity extends AppCompatActivity {
@Inject
Student student;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test1);
ButterKnife.bind(this);
initDatas();
}
private void initDatas() {
DaggerTest1Component.builder()
.test1Module(new Test1Module(this))
.build()
.inject(this);
}
@OnClick(R.id.btn1)
public void onViewClicked() {
Toast.makeText(this, student.toString(), Toast.LENGTH_SHORT).show();
}
}
然后运行代码点击 Button,神奇的事情发生了:
![](https://static001.geekbang.org/infoq/f8/f8a957599c6325c72db49079140afc85.jpeg?x-oss-process=image/resize,p_80/auto-orient,1)
显然,添加了两个看起来莫名其妙的 Module 和 Component 类,然后在 Activity 中添加一段代码,被 @Inject 的 Student 类被成功依赖注入到了 Activity 中,我们接下来就可以肆无忌惮使用这个 Student 对象了!
3、我们为什么使用依赖注入
=============
这时候不可避免的,有些同学会有些疑问,我们为什么要花费这么大的力气(时间成本)去学习这样一个看起来很鸡肋的 Dagger 呢?我们需要一个 Student 对象,完全可以直接通过 new 的方式创建一个嘛!
当然是有必要的,因为通常简单的代码具有耦合性,而要想降低这样的耦合就需要其他的辅助代码,其实少代码量和低耦合这两者并不能同时兼顾。
试想,我们如果通过这样的方式,在其他的文件中创建了这样若干个(心大一些,我们是一个大项目的唯一负责人),不,1000 个文件中使用到了 Student 对象,我们至少要 new 1000 个新的 Student 类对象,这时,新的需求到了,我们的 Student 需要添加一个 String 类型的参数 name。
what the fuck? 这意味我需要分别跑这 1000 个文件中逐个修改 new Student()的那行代码吗?
如果是 Dagger2,当然不需要,我们只需要在 Student 类中做出简单的修改即可,这在后文中将会提到(因为涉及参数问题),我们只需要知道只需轻松几步即可完成 Student 的构造修改问题,达到低耦合的效果即可。
4、Module 和 Component 作用详解
======================
现在我们具体的思考以下一个场景:
我们假设案例中的 Activity 代表家庭住址,Student 代表某个商品,现在我们需要在家(Activity)中使用商品(Student),我们网购下单,商家(代表着案例中自动生成的 Student_Factory 工厂类)将商品出厂,这时我们能够在家直接获得并使用商品吗?
当然不可能,虽然商品(Student)已经从工厂(Factory)生产出来,但是并没有和家(Activity)建立连接,我们还需要一个新的对象将商品送货上门,这种英雄级的人物叫做——快递员(Component,注入器)。
没错,我们需要这样的一种注入器,将已经生产的 Student 对象传递到需要使用该 Student 的容器 Activity 中,于是我们需要在 Activity 中增加这样几行代码:
DaggerTest1Component.builder()
评论