概述
最近公司的项目是用mvp+dagger2搭的框架,由于之前没接触过dagger2,改bug和做需求总是一脸懵逼,看了些文档介绍,和大多数学习者一样从Dependency Injection、注解概念等等开始了解,然后敲代码上手,在此记录下学习心得。既然是入门,那些概念和注解的历史就不介绍了,Google一下你就知道,直接介绍最最基本的使用以及具体实现原理。
@Inject、@Component
先看一个例子,MainActivity依赖Province,Province依赖City,City依赖Street;
使用前
Street.java
public class Street {
public Street(){}
public String show(){
return "人民南路";
}
}
City.java
public class City {
public Street street;
public City(Street street) {
this.street = street;
}
public String show() {
return "成都市" + street.show();
}
}
Province.java
public class Province {
public City city;
public Province(City city) {
this.city = city;
}
public String showAddress() {
return "四川省" + city.show();
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
public Street street;
public City city;
public Province province;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
street = new Street();
city = new City(street);
province = new Province(city);
Log.d("hcy", "onCreate: " + province.showAddress());
}
}
可以看到,为了获取地址信息,在代码中需要实例化各种依赖到的对象,一旦依赖过多就容易影响代码阅读,那么配合使用@Inject和@Component又是怎样的呢?
使用后
1.首先在build.gradle中添加依赖
dependencies {
compile 'com.google.dagger:dagger-android:2.11'
compile 'com.google.dagger:dagger-android-support:2.11' // if you use the support libraries
annotationProcessor 'com.google.dagger:dagger-android-processor:2.11'
annotationProcessor 'com.google.dagger:dagger-compiler:2.11'
}
Street.java
public class Street {
@Inject
public Street(){}
public String show(){
return "人民南路";
}
}
2.需要依赖的成员和提供依赖的成员构造函数用@Inject标注
City.java
public class City {
@Inject
public Street street;
@Inject
public City(Street street) {
this.street = street;
}
public String show() {
return "成都市" + street.show();
}
}
Province.java
public class Province {
@Inject
public City city;
@Inject
public Province(City city) {
this.city = city;
}
public String showAddress() {
return "四川省" + city.show();
}
}
3.需要额外加一个接口MainActivityComponent.java,用@Component标注
@Component
public interface MainActivityComponent {
void inject(MainActivity activity);
}
4.执行DaggerMainActivityComponent.create().inject(this)
此时的MainActivity.java
public class MainActivity extends AppCompatActivity {
@Inject
public Province province;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//in Android Studio, select Build > Rebuild Project
DaggerMainActivityComponent.create().inject(this);
Log.d("hcy", "onCreate: " + province.showAddress());
}
}
前后的打印是一致的,可以看到MainActivity的中原本需要实例化对象的那些代码现在可以省略了,有助于我们更好地关注业务实现。
小结
回顾下使用注解的步骤:
1.build.gradle中添加dagger2依赖
2.使用@Inject标注在构造函数和被引用的成员变量上
3.新建MainActivityComponent接口,并用@Component标注
4.在MainActivity中执行DaggerMainActivityComponent.create().inject(this);(第一次需Rebuild Project)
源码分析
我们在MainaActivity中加了DaggerMainActivityComponent.create().inject(this)这句代码替换了之前的一些实例化的操作,那么这句代码具体做了哪些工作?原来Dagger2会在编译过程中生成对应的依赖项,这些依赖项在Android Studio该路径下,如图所示:
DaggerMainActivityComponent.create()
public static MainActivityComponent create() {
return builder().build();
}
public static Builder builder() {
return new Builder();
}
public static final class Builder {
private Builder() {
}
public MainActivityComponent build() {
return new DaggerMainActivityComponent(this);
}
}
可以看到,不管是通过builder().build()还是create(),最后都会调用DaggerMainActivityComponent构造函数;在通过源码阅读后,可以将整个过程分为两步,分别是initialize()和inject()
initialize()
private DaggerMainActivityComponent(Builder builder) {
assert builder != null;
initialize(builder);
}
private void initialize(final Builder builder) {
this.cityMembersInjector = City_MembersInjector.create(Street_Factory.create());//注释1
this.cityProvider = City_Factory.create(cityMembersInjector, Street_Factory.create());//注释2
this.provinceMembersInjector = Province_MembersInjector.create(cityProvider);//注释3
this.provinceProvider = Province_Factory.create(provinceMembersInjector, cityProvider);//注释4
this.mainActivityMembersInjector = MainActivity_MembersInjector.create(provinceProvider);//注释5
}
在initialize()方法中对成员赋值,这里的成员分为两类,分别以_MembersInjector和_Factory为后缀;Xx_MembersInjector可以理解为注入器,用来实现Xx与内部成员的依赖:
public interface MembersInjector<T> {
void injectMembers(T instance);
}
Xx_Factory是创建Xx的工厂:
public interface Provider<T> {
T get();
}
public interface Factory<T> extends Provider<T> {
}
这里先记住两者的功能,具体后面会分析
注释1
先看下Street_Factory.java
public final class Street_Factory implements Factory<Street> {
private static final Street_Factory INSTANCE = new Street_Factory();
@Override
public Street get() {
return new Street();
}
public static Factory<Street> create() {
return INSTANCE;
}
}
代码很简单,通过恶汉式创建了一个Street_Factory单例(这里的源码可能会有不同,之前看过一版是通过枚举创建的单例);再看下City_MembersInjector.java
public final class City_MembersInjector implements MembersInjector<City> {
private final Provider<Street> streetProvider;
public City_MembersInjector(Provider<Street> streetProvider) {
assert streetProvider != null;
this.streetProvider = streetProvider;
}
public static MembersInjector<City> create(Provider<Street> streetProvider) {
return new City_MembersInjector(streetProvider);
}
@Override
public void injectMembers(City instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.street = streetProvider.get();
}
}
City_MembersInjector只是将传进来的Street_Factory赋值给自己的成员变量;
注释2
后面的功能都一样,就用伪代码列出:
City_Factory.java
public final class City_Factory implements Factory<City> {
public City_Factory(...省略参数){
this.city_MembersInjector = city_MembersInjector;
this.Street_Factory = street_Factory;
}
}
注释3
Province_MembersInjector.java
public final class Province_MembersInjector implements MembersInjector<Province> {
public Province_MembersInjector(...省略参数){
this.city_Factory = city_Factory
}
}
注释4
Province_Factory.java
public final class Province_Factory implements Factory<Province> {
public Province_Factory(...省略参数){
this.province_MembersInjector = province_MembersInjector
this.city_Factory = city_Factory
}
}
注释5
public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
private final Provider<Province> provinceProvider;
public MainActivity_MembersInjector(Provider<Province> provinceProvider) {
this.province_Factory = provinceProvider;
}
}
到此目标工厂和注入器都已经创建完成,但是此时目标对象和依赖关系还没产生。
inject()
inject()通过调用injectMembers()完成真正目标对象实例化以及依赖操作,代码也没多少,就把整个完整的过程涉及到的代码贴出来:
//DaggerMainActivityComponent.java
public void inject(MainActivity activity) {
mainActivityMembersInjector.injectMembers(activity);
}
//MainActivity_MembersInjector.java
public void injectMembers(MainActivity instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.province = provinceProvider.get();
}
//Province_Factory.java
public Province get() {
return MembersInjectors.injectMembers(
provinceMembersInjector, new Province(cityProvider.get()));
}
//Province_MembersInjector.java
public void injectMembers(Province instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.city = cityProvider.get();
}
//City_Factory.java
public City get() {
return MembersInjectors.injectMembers(cityMembersInjector, new City(streetProvider.get()));
}
//City_MembersInjector.java
public void injectMembers(City instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.street = streetProvider.get();
}
//Street_Factory.java
public Street get() {
return new Street();
}
可以看到inject()和initialize()刚好是相反的过程,直到找到依赖的源头完成源头对象的实例化,即这里的Street_Factory的get()方法的返回值;在Street实例化完成之后返回给City完成City实例化,City实例化完之后对自己的成员重新赋值了一遍,即产生依赖关系:
//City_MembersInjector.java
public void injectMembers(City instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.street = streetProvider.get();
}
instance.street赋值的时候调用了streetProvider.get(),这是一个怎样的过程呢,还是画个图吧
step1的时候已经完成了Street和City的实例化,接着执行instance.street = streetProvider.get(),这句代码即下面step2的过程:
红色的就变成垃圾了,所以在这个过程中Street被new了两次;继续分析创建Province以及建立依赖的过程:
可以看到在整个过程中Street其实new了4次,如果依赖更多的话(比如最外层再加个Country),Street在内存中被new的次数也会*2,不过过程中这些对象最终都会变成垃圾被回收(总觉得这是额外的开销,依赖多了岂不是编译就慢了?),而不用dagger2只要new一次就可以了(如下图),但是两者最终都是一条依赖链
总结
整个流程:
黑色的流程线是initialize()的过程,用来创建目标实例的工厂和注入器;红色流程线是inject()/injectMembers()的过程,用来创建目标实例以及实现依赖。最后在回过头来看下@inject和@component这两个标注,可以得出一些结论:
1.若一个类(Xx)的构造函数被@inject标注,则该类编译时会产生Xx_Factory;
2.若一个类中的成员变量被@inject标注,则该类编译时会产生Xx_MembersInjector;
3.@Component标注的接口(Xx)在编译时生成DaggerXx,负责将上面两个联系起来。
一个图
@Module和@Provides
学习这两个注解,配合@Inject和@Component使用完成和上面一样的功能
使用前
和上面的使用前代码一致
使用后
1.创建一个Module.java,这里取名BeanModule.java(想怎么取名字都可以,比如周杰伦.java,但是最好别这么干,官方推荐以Module为后缀),并用@Module标注,创建目标实例的方法用@Provides标注(官方推荐方法名以provide为前缀)如下:
@Module
public class BeanModule {
@Provides
Street providerStreet() {
return new Street();
}
@Provides
City providerCity(Street street) {
return new City(street);
}
@Provides
Province providerProvince(City city) {
return new Province(city);
}
}
2.修改上面的MainActivityComponent.java接口,@Component后面多了一个配置,如下:
@Component(modules = BeanModule.class)
public interface MainActivityComponent {
void inject(MainActivity activity);
}
modules的作用相当于告诉Component,当你需要创建对象的时候就到我这里来拿。MainActivity不变,运行后的效果和上面一毛一样。
源码分析
先看编译过程生成的文件:
同样从DaggerMainActivityComponent.create().inject(this),这句代码开始分析
DaggerMainActivityComponent.create()
public static MainActivityComponent create() {
return new Builder().build();
}
public static final class Builder {
private BeanModule beanModule;
private Builder() {}
public MainActivityComponent build() {
if (beanModule == null) {
this.beanModule = new BeanModule();
}
return new DaggerMainActivityComponent(this);
}
public Builder beanModule(BeanModule beanModule) {
this.beanModule = Preconditions.checkNotNull(beanModule);
return this;
}
}
不管是通过builder().build()还是create(),最后都会调用DaggerMainActivityComponent构造函数;和上面对比这里多了一步,那就是创建了BeanModule实例,还是把这个过程分成两步:initialize()和inject()
initialize()
private DaggerMainActivityComponent(Builder builder) {
assert builder != null;
initialize(builder);
}
private void initialize(final Builder builder) {
this.providerStreetProvider = BeanModule_ProviderStreetFactory.create(builder.beanModule);
this.providerCityProvider =
BeanModule_ProviderCityFactory.create(builder.beanModule, providerStreetProvider);
this.providerProvinceProvider =
BeanModule_ProviderProvinceFactory.create(builder.beanModule, providerCityProvider);
this.mainActivityMembersInjector =
MainActivity_MembersInjector.create(providerProvinceProvider);
}
initialize()方法对自己的成员赋值,创建了目标对象工厂和注入器,在这里的注入器只有一个MainActivity_MembersInjector;
inject()
//DaggerMainActivityComponent.java
public void inject(MainActivity activity) {
mainActivityMembersInjector.injectMembers(activity);
}
//MainActivity_MembersInjector.java
public void injectMembers(MainActivity instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.province = provinceProvider.get();
}
//...省略中间的部分get()
//BeanModule_ProviderStreetFactory.java
public Street get() {
return Preconditions.checkNotNull(
module.providerStreet(), "Cannot return null from a non-@Nullable @Provides method");
}
这里的inject()和injectMembers()的工作和上面是一样的,调用BeaModule中的方法创建需要的实例,若该实例在创建时依赖其他实例,则调用BeaModule中的对应方法先创建依赖实例,直到找到源头,最大的区别就是在Xx_Factory中少了一步重新创建依赖的过程。用一张图来表示创建的过程就是
可以看到,相比于上面那种在过程中new很多实例的做法,这种做法显然更加高效。多说一点,在这些生成的XX_Factory中还多了一个静态方法,如:
//BeanModule_ProviderStreetFactory.java
/** Proxies {@link BeanModule#providerStreet()}. */
public static Street proxyProviderStreet(BeanModule instance) {
return instance.providerStreet();
}
//BeanModule_ProviderProvinceFactory.java
/** Proxies {@link BeanModule#providerProvince(City)}. */
public static Province proxyProviderProvince(BeanModule instance, City city) {
return instance.providerProvince(city);
}
因此我们可以在MainActivity直接通过类名调用创建实例。
用图来表示@Module、@Providers、@Component、@Inject之间的联系:
Why
既然用@Inject和@Component就能够完成的功能,为啥我还要多写那些Module类和provider方法,别跟我说是因为编译过程中比较费时?原来@Inject并不是万能的,官网的介绍:
But
@Inject
doesn’t work everywhere:
- Interfaces can’t be constructed.
- Third-party classes can’t be annotated.
- Configurable objects must be configured!
如果我们要注入的对象是个接口,接口不能被实例化;或者是我们要注入的对象是第三方库,我们没法把@Inject标注在三方库的构造函数上,真是这样的么?就拿接口举例:
改改上面的例子,添加一个IShow接口
public interface IShow {
String show();
}
修改City.java
public class City implements IShow {
@Inject
public Street street;
@Inject
public City(Street street) {
this.street = street;
}
@Override
public String show() {
return "成都市" + street.show();
}
修改Province.java,让它接收一个接口
public class Province {
@Inject
public IShow city;
@Inject
public Province(IShow city) {
this.city = city;
}
public String showAddress() {
return "四川省" + city.show();
}
}
MainActivity不变,跑起来果然GG了
而配合使用@Module和@Provides则不会出现这种情况。问题又来了,如果依赖的提供方同时存在以@Inject标注和以@Module、@Providers标注,会找哪个?回到代码中,把两种写法都补充完整,要回答这个问题还是只有看源码了,通过编译可以看到生成的文件
这里把两个方案都导进来了,随时待命,再看下DaggerMainActivityComponent.java的initialize()方法:
private void initialize(final Builder builder) {
this.providerStreetProvider = BeanModule_ProviderStreetFactory.create(builder.beanModule);
this.providerCityProvider =
BeanModule_ProviderCityFactory.create(builder.beanModule, providerStreetProvider);
this.providerProvinceProvider =
BeanModule_ProviderProvinceFactory.create(builder.beanModule, providerCityProvider);
this.mainActivityMembersInjector =
MainActivity_MembersInjector.create(providerProvinceProvider);
}
可以发现和上面分析@Module、@Providers的源码是一样的,之后的流程当然也一样。因此如果两种同时存在,会选择@Module、@Providers,而另一种只是个“备胎”...
学习资料
当然dagger2还有很多很强大的功能,待续