好久没有更新博客了,最近在学习依赖注入框架Dagger2,感觉上手还是挺不容易的的,也查看了网上好多朋友写的技术文章,折磨了好长时间~今天,我在他们基础上,打算按照边撸代码边分析源码的模式去学习Dagger2。
首先简单介绍一下Dagger2,它是一个依赖注入(Dependency Injection)框架,和我们平时使用的ButterKnife类似,粗略的理解就是帮助我们创建对象然后赋值的框架,省去了手动创建对象的操作,当然它的作用不仅仅是这些,我们回头再说。好,开始动手~
在项目中集成Dagger2的相关配置:
- 在项目的build.gradle文件中dependencies节点下添加:
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' - 然后在app的build.grale文件中添加:
apply plugin:'com.neenbedankt.android-apt'
以及dependencies节点下添加:
compile'com.google.dagger:dagger:2.4'
compile'org.glassfish:javax.annotation:10.0-b28'
apt'com.google.dagger:dagger-compiler:2.4'
来点理论基础,先介绍下Dagger2中两个基本的注解:
- @Inject:Inject有两个作用,一个是使用在构造方法上,通过标记构造方法让Daggger2来使用从而提供依赖(即Dagger在创建该类对象时使用该构造方法中),另一个作用就是标记在需要依赖的变量让Dagger2为其提供依赖,即让Dagger2帮助创建对象依赖。
- @Component:用来标注接口,被标注了Component的接口在编译时会产生相应的类的实例来作为提供依赖方和需要依赖方之间的桥梁,把相关依赖注入到其中。
依旧很迷糊,很正常,废话少说,下面开始撸代码:
- 新建一个Person类,代码如下:
public class Person {
@Inject
public Person() {
}
public String getDesc() {
return "hello world";
}
}
很简单的类,只是在构造器上多添加了@Inject注解。
- 在MainActivity中添加:
public class MainActivity extends AppCompatActivity {
@Inject
Person person;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
注意Person不能为private,后面可以通过源码解释为什么。
- 新建一个接口,就叫做MainComponent:
@Component
public interface MainComponent {
void inject(MainActivity activity);
}
- 到这里咱们需要make project或者rebuild一下。然后再回到MainActivity中,添加代码:
public class MainActivity extends AppCompatActivity {
@Inject
Person person;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerMainComponent.builder().build().inject(this);
Log.e("TAG", "person getDesc() = " + person.getDesc());
}
}
然后运行,查看输出结果:
发现,程序没有崩溃,没有出现空指针异常,说明我们的person已经被初始化并指向了一个对象,一块内存区域,但是很明显我们的代码中根据没有new对象,到这里应该能理解Dagger2是个什么东西了吧~是不是和ButterKnife一样,当然ButterKinfe不是为我们创建对象,真正创建对象的操作在setContentView()中执行的,只是为我们省去了findViewById的操作。那Dagger2到底是怎么实现这样的神奇功效呢?我们来看看源码(找到对应module下的build/generated/source/apt/debug),会发现这里有三个Java源文件:
可以稍微看看类的结构,这些都是在编译阶段产生的:
- DaggerMainComponent:对应于我们之前创建的MainComponent接口,它实际上就是MainComponent的实现类,同样我们可以看出命名规则,即Dagger+接口名。
- Person_Factory:发现它是一个枚举类,而且见名之意,它是工厂。
- MainActivity_MemberInjector:待会儿源码分析时再说。
- 下面开始源码分析,先从MainActivity中的DaggerMainComponent.builder().build().inject();这行代码入手。
public static Builder builder() {
return new Builder();
}
public static final class Builder {
private Builder() {}
public MainComponent build() {
return new DaggerMainComponent(this);
}
}
实际上也就是造了个DaggerMainComponent对象。
private DaggerMainComponent(Builder builder) {
assert builder != null;
initialize(builder);
}
@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
this.mainActivityMembersInjector = MainActivity_MembersInjector.create(Person_Factory.create());
}
先看Person_Fatory.create(),很简单,就是返回了一个Person_Fatory的枚举实例(也就是个单例工厂)。但是就Person_Factory类本身,它实现了Factory接口,而Factory又是继承自Provider(有抽象方法get)。接着看MainActivity_MemberInjector.create()方法的具体实现:
public static MembersInjector<MainActivity> create(Provider<Person> personProvider) {
return new MainActivity_MembersInjector(personProvider);
}
public MainActivity_MembersInjector(Provider<Person> personProvider) {
assert personProvider != null;
this.personProvider = personProvider;
}
看这个create方法的参数是Provider,多态很好理解。最终,把传入的参数值赋给了成员变量personProvider,这样,MainActivity_MemberInjector对象就持有了Person_Factory的引用了。再回到DaggerComponent的initialize()方法,发现它将create方法的返回值赋给了成员变量mainActivityMembersInjector。那到这里,三个编译阶段产生的类之间的关系也就弄清楚了。DaggerMainComponent内部持有MainActivity_MemberInjecot的对象引用,而MainActivity_MemberInjecot内部又持有Person_Factory的引用,都是组合关系。
最后,再来看看关键的inject方法里到底执行了什么操作(感觉前面的分析就是为了它做铺垫的)。
@Override
public void inject(MainActivity activity) {
mainActivityMembersInjector.injectMembers(activity);
}
再跟进去,发现跟到了接口的定义处了。那么我们现在就要找找mainActivityMembersInjector这个父类型的引用到底指向哪个子类型的对象了。前面已经提到,就是MainActivity_MemberInjector。
@Override
public void injectMembers(MainActivity instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.person = personProvider.get();
}
这几行代码,首先是判空操作,理解起来没多大意义。重点是instance.person = personProvider.get();这个personProvider,实际是谁?想一想:就是前面分析到的Person_Factory,它的get方法:
@Override
public Person get() {
return new Person();
}
就是new了一个Person对象。然后赋给了instance.person,那么这行代码很好证明了前面提到的在定义Person person时,不能加上访问修饰符private,最起码应该是包访问级别的。
至此,关于Dagger2中涉及两个基本注解@Inject和@Component就源码分析结束了。再来看看自动生成的三个类:DaggerMainComponent像是对外的统一入口和代表,或者说是Dagger2中任务的发起者,MainActivity_MemberInjector承担的是分析问题然后细化工作的一个角色,而最终的落实者还是各个工厂(Person_Factory)。