依赖注入是一种面向对象的编程模式,它的出现是为了降低耦合性。可能有的人觉得之前并没有使用过依赖注入,其实当我们在类的构造函数中通过参数引入对象或通过set方法设置类的对象时,就是依赖注入。而通过注解的方式完成的依赖注入就是本篇要讲的Dagger库的使用。
添加依赖build.gradle
compile 'com.google.dagger:dagger:2.7'
annotationProcessor 'com.google.dagger:dagger-compiler:2.7'
*如果,build项目抛出“ Execution failed for task ':app:javaPreCompileDebug” 就在gradle中填写下面代码。
defaultConfig {
javaCompileOptions { annotationProcessorOptions { includeCompileClasspath = true } }
}
1、 创建Student类:
public class Student {
@Inject
public Student() {
}
}
Student类中有一个空的构造方法,并且在构造方法上面添加了一个@Inject注解。
然后,我们使用ctrl+F9(mac使用Cmd+F9)进行一次编译,查看项目路径:
app\build\generated\source\apt\debug\com\mei_husky\sample_dagger2\model\Student_Factory.java
会发现编译器自动生成了一个Student_Factory类:
@Generated(
value = "dagger.internal.codegen.ComponentProcessor",
comments = "https://google.github.io/dagger"
)
public enum Student_Factory implements Factory {
INSTANCE;
@Override
public Student get() {
return new Student();
}
public static Factory create() {
return INSTANCE;
}
}
一个工厂类,在通过create()创建后,每次调用get()方法都能获得一个Student对象。
小结:通过@Inject注解了一个类的构造方法后,可以让编译器帮助我们产生一个对应的Factory类,通过这个工厂类我们可以通过简单的get()方法获取到Student对象。
2、调用Student类:
public class A01SimpleActivity extends AppCompatActivity {
@BindView(R.id.btn_01)
Button btn01;
@Inject
Student student;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_a01_simple);
ButterKnife.bind(this);
}
@OnClick(R.id.btn_01)
public void onViewClicked(View view) {
switch (view.getId()){
case R.id.btn_01:
Toast.makeText(this,student.toString(),Toast.LENGTH_SHORT).show();
break;
}
}
}
Activity类中创建一个成员变量Student,按照Dagger2给我们的指示,当我们需要一个Student,我们只需要在这个成员变量上方加一个@Inject注解,编译器会自动帮我们产生对应的代码,我们就可以直接使用这个Student对象了!运行代码,并点击Button直接报空指针异常:
小结:@Inject并没有帮助我们初始化对应的Student对象,或者说,我们的Activity并没有使用刚才我们看到的Student_Factory类。这是因为缺少建立Activity和Student_Factory类之间的关系。
3、Module和Component使用
接下来创建Module类、Component接口:
@Module
public class A01SimpleModule {
private A01SimpleActivity activity;
public A01SimpleModule(A01SimpleActivity activity) {
this.activity = activity;
}
}
@Component(modules = A01SimpleModule.class)
public interface A01SimpleComponent {
void inject(A01SimpleActivity activity);
}
继续在之前Activty中添加下面一段代码,运行后调用成功。
public class A01SimpleActivity extends AppCompatActivity {
@BindView(R.id.btn_01)
Button btn01;
@Inject
Student student;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_a01_simple);
ButterKnife.bind(this);
//新添代码
DaggerA01SimpleComponent.builder()
.a01SimpleModule(new A01SimpleModule(this))
.build()
.inject(this);
}
@OnClick(R.id.btn_01)
public void onViewClicked(View view) {
switch (view.getId()){
case R.id.btn_01:
Toast.makeText(this,student.toString(),Toast.LENGTH_SHORT).show();
break;
}
}
}
小结:添加了Module和Component类,然后在Activity中添加一段代码,被@Inject的Student类被成功依赖注入到了Activity中,并且其他地方也可以使用上面一段代码调用Student类,这样就降低了Student的耦合度。
这时候不可避免的,有些同学会有些疑问,我们需要一个Student对象,完全可以直接通过new的方式创建一个嘛?这是因为通常简单的代码具有耦合性,而要想降低这样的耦合就需要其他的辅助代码,其实代码量和耦合度这是两码事。试想,我们如果通过这样的方式,在其他的文件中创建1000个文件使用Student对象,那么至少要new 1000个新的Student类对象。这时,新的需求到了,Student类需要改动内部属性和字段, 可能需要分别对这1000个文件中逐个修改调用的代码。如果使用Dagger2,我们只需要在Student类中做出简单的修改即可轻松完成对Student的构造修改问题,达到低耦合的效果即可。
假设,案例中的Activity——代表学校,Student——代表某学生。现在学校(Activity)要开学了,需要学生(Student)去报名。OK,接下来,学生肯定要在家整理行李(家代表着自动生成的Student_Factory)。虽然这位学生(Student)已经从家(Student_Factory)准备出发了,但是距离学校(Activity)还是有一段距离的,肯定需要一种交通方式(Component)。
交通方式(Component)就是一种注入器,将学生(Student)送到学校(Activity)。即如下代码:
DaggerA01SimpleComponent.builder()
.a01SimpleModule(new A01SimpleModule(this))
.build()
.inject(this);
这时可能会问,Module到底干什么的呢?
首先,我们可以注释调Module的代码,结果,发现我们依然可以正常使用Student对象。
DaggerA01SimpleComponent.builder()
//.a01SimpleModule(new A01SimpleModule(this))//注释掉这行代码
.build()
.inject(this);
我们可以暂时可以这样理解Module,它的作用就是交通方式的一种叫“公交车”。学生乘坐公交车就可以去学校,比较好理解。
然后,重写Student类,取消了@Inject注解。
public class Student {
//取消注解
public Student() {
}
}
Module类添加代码:
@Module
public class A01SimpleModule {
private A01SimpleActivity activity;
public A01SimpleModule(A01SimpleActivity activity) {
this.activity = activity;
}
//下面为新增代码:
@Provides
Student provideStudent(){
return new Student();
}
}
最后,Component类代码不变,在Activity中调用Student对象,结果依然正常。
DaggerA01SimpleComponent.builder()
.a01SimpleModule(new A01SimpleModule(this))
.build()
.inject(this);
小结:原因是,虽然@Inject注解取消了,但是,已经在公交车(Module)上强行提供 @Providers 一个学生进行乘坐,然后学生被这种交通方式(Component)送到学校(Activity)。
经过简单的使用Dagger2,我们已经可以基本有了以下了解:
————————————————————————————
本文参考:Android 神兵利器Dagger2使用详解(一)基础使用
源码请看: Android 神兵利器Dagger2使用详解(二)Module&Component源码分析