Dagger2从入门到补胎(一)

概述

最近公司的项目是用mvp+dagger2搭的框架,因为以前没接触过dagger2,改bug和作需求老是一脸懵逼,看了些文档介绍,和大多数学习者同样从Dependency Injection、注解概念等等开始了解,而后敲代码上手,在此记录下学习心得。既然是入门,那些概念和注解的历史就不介绍了,Google一下你就知道,直接介绍最最基本的使用以及具体实现原理。html

@Inject、@Component

先看一个例子,MainActivity依赖Province,Province依赖City,City依赖Street;java

使用前

Street.javaandroid

public class Street {
    public Street(){}

    public String show(){
        return "人民南路";
    }
}复制代码

City.javagit

public class City {
    public Street street;
    public City(Street street) {
        this.street = street;
    }

    public String show() {
        return "成都市" + street.show();
    }
}复制代码

Province.javagithub

public class Province {
    public City city;

    public Province(City city) {
        this.city = city;
    }

    public String showAddress() {
        return "四川省" + city.show();
    }
}复制代码

MainActivity.javabash

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中添加依赖ide

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还有不少很强大的功能,待续

github dagger介绍

dagger官网

相关文章
相关标签/搜索