本次使用说明是以 GitHub 上 Dagger 仓库 2.15版本来进行的分析,同时以官方网站文档作为参考。如后续版本有较大改变,恕不另行修改

上一篇中,我们了解控制反转设计原则,注入依赖概念,并学习了Dagger2 的最基本用法,本篇我们来说说 Dagger2 的另外一些重要功能。

@Named 与 @Qualifier

接上一篇的配置依赖,假定现有 @Inject 修饰的是一个接口,那么注入的依赖一定是此接口的一个实例,现在假定继承关系如下:

//自定义接口
public interface inter {
  void print();
}
//自定义实现类A
public class A implements inter {
  @Inject
  publicA() {
  }

  public void print(){
    Log.i(ARIRUS, "print: A ");
  }
}
//自定义实现类B
public class B implements inter {

  String mString;

  @Inject
  public B() {
    mString = "";
  }

  public void print(){
    Log.i(ARIRUS, "print: B "+mString);
  }
}

那么在Module 和 Main2Activity的注入中,有如下配置:

@Module
public class Main2Module {
  @Provides
  public static inter provideInterB(){ return  new B(); }

  @Provides
  public static inter provideInterA(){ return  new A(); }
}


public class Main2Activity extends AppCompatActivity {

  public static final String ARIRUS = "Main2Activity";

  public static void start(Context context) {
      Intent starter = new Intent(context, Main2Activity.class);
      context.startActivity(starter);
  }

  @Inject
  inter mInter;

  @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main2);

    DaggerMain2Component.create().inject(this);
  }
}

运行编译会报错:

Error:(24, 8) 错误: com.arirus.daggerapplication.bean.inter is bound multiple times:
@Provides com.arirus.daggerapplication.bean.inter com.arirus.daggerapplication.modules.Main2Module.provideInterB()
@Provides com.arirus.daggerapplication.bean.inter com.arirus.daggerapplication.modules.Main2Module.provideInterA()

就是说注入生成依赖实例时有两个返回类型一摸一样的@Provides 方法,不知道该生成哪个。此时 @Named 与 @Qualifier 便可以起作用了。

@Named

@Named 与 @Qualifier 都属于Java 依赖注入标准(JSR-330)简介定义的注解。用于标注依赖实例。

@Module
public class Main2Module {
  @Provides
  @Named("B")
  public static inter provideInterB(){ return  new B(); }

  @Provides
  @Named("A")
  public static inter provideInterA(){ return  new A(); }
}

public class Main2Activity extends AppCompatActivity {
  ...  
  @Inject
  @Named("A")
  inter mInter;
  ...
}

这样便限定了注入生成的 inter 一定是 A 的实例。从而解决了上述问题。 注意:刚才我们所说的都是针对 Component 中 Members-injection methods,就是通过注入到参数中每个被@Inject修饰的字段或者方法并不适用于 Component 的 Provides methods。

@Qualifier

@Named 注解以字符串的形式来定义每个名字,使用 @Qualifier 元注解可以更好的定义注解以区分依赖。

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface Color {
  ColorE color() default ColorE.TAN;
  public enum ColorE { RED, BLACK, TAN }
}

@Module
public class Main2Module {
  ...

  //@Named("B")
  @Color(color = Color.ColorE.BLACK)
  @Provides
  public static inter provideInterB(){ return  new B(); }

  @Color(color = Color.ColorE.RED)
  //@Named("A")
  @Provides
  public static inter provideInterA(){ return  new A(); }

  ...
}

public class Main2Activity extends AppCompatActivity {
  ...  
  @Inject
  @Color(color = Color.ColorE.RED)
  inter mInter;
  ...
}

Qualifier 与 Named 两个注解的功能是一样的,都是为了区分依赖,更推荐前者。

@Scope 与 @Singleton

Scope 也是一个元注解,用来确定注入的实例的生命周期的,如果没有使用 Scope 注解,Component 每次调用 Module 中的 provide 方法或 Inject 构造函数生成的工厂时都会创建一个新的实例,而使用 Scope 后可以复用之前的依赖实例。Singleton 是 Scope 的一个实现。 这里我们举个例子来说明:

//定义 MyScope 注解
@Documented
@Retention(RUNTIME)
@Scope
public @interface MyScope {
}

//MyScope 修饰 Component
@Component(modules = {Main2Module.class})
@MyScope
public interface Main2Component {
  void inject(Main2Activity activity);
  A  getA();
  B  getB();
}

//Provides methods 使用 MyScrope 修饰
@Module
public class Main2Module {

  @MyScrope
  @Provides
  public A provideA(){
    return new A();
  }

  @Provides
  public B provideB(){
    return new B();
  }

}

//Activity 中的调用
@Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main2);

    Main2Component component = DaggerMain2Component.create();
    for (int i = 0; i < 5; i++) {
      Log.i(ARIRUS, "onCreate: "+component.getA());
      Log.i(ARIRUS, "onCreate: "+component.getB());
    }
}

这里直接打印5个新的 A 、B 对象。结果如下

05-02 18:05:44.902 15152-15152/? I/Main2Activity: onCreate: com.arirus.daggerapplication.bean.A@28d434e1
05-02 18:05:44.902 15152-15152/? I/Main2Activity: onCreate: com.arirus.daggerapplication.bean.B@117d4206
05-02 18:05:44.902 15152-15152/? I/Main2Activity: onCreate: com.arirus.daggerapplication.bean.A@28d434e1
05-02 18:05:44.902 15152-15152/? I/Main2Activity: onCreate: com.arirus.daggerapplication.bean.B@331720c7
05-02 18:05:44.902 15152-15152/? I/Main2Activity: onCreate: com.arirus.daggerapplication.bean.A@28d434e1
05-02 18:05:44.902 15152-15152/? I/Main2Activity: onCreate: com.arirus.daggerapplication.bean.B@2dbfc6f4
05-02 18:05:44.902 15152-15152/? I/Main2Activity: onCreate: com.arirus.daggerapplication.bean.A@28d434e1
05-02 18:05:44.902 15152-15152/? I/Main2Activity: onCreate: com.arirus.daggerapplication.bean.B@3dfe3e1d
05-02 18:05:44.902 15152-15152/? I/Main2Activity: onCreate: com.arirus.daggerapplication.bean.A@28d434e1
05-02 18:05:44.902 15152-15152/? I/Main2Activity: onCreate: com.arirus.daggerapplication.bean.B@39b7892

可以看到,getA() 得到的都是同一个 A 对象,但是对于getB() 得到的对象都不一样。我们可以看一下编译的生成的文件:

public final class DaggerMain2Component implements Main2Component {
  private Main2Module main2Module;

  private Provider<A> provideAProvider;

  ...

  @SuppressWarnings("unchecked")
  private void initialize(final Builder builder) {
    this.provideAProvider =
        DoubleCheck.provider(Main2Module_ProvideAFactory.create(builder.main2Module));
    this.main2Module = builder.main2Module;
  }

  @Override
  public void inject(Main2Activity activity) {
    injectMain2Activity(activity);
  }

  @Override
  public A getA() {
    return provideAProvider.get();
  }

  @Override
  public B getB() {
    return Main2Module_ProvideBFactory.proxyProvideB(main2Module);
  }
  ...
}

//DoubleCheck 的实现
public final class DoubleCheck<T> implements Provider<T>, Lazy<T> {
  private static final Object UNINITIALIZED = new Object();

  private volatile Provider<T> provider;
  private volatile Object instance = UNINITIALIZED;

  private DoubleCheck(Provider<T> provider) {
    assert provider != null;
    this.provider = provider;
  }

  @SuppressWarnings("unchecked") // cast only happens when result comes from the provider
  @Override
  public T get() {
    Object result = instance;
    if (result == UNINITIALIZED) {
      synchronized (this) {
        result = instance;
        if (result == UNINITIALIZED) {
          result = provider.get();
          /* Get the current instance and test to see if the call to provider.get() has resulted
           * in a recursive call.  If it returns the same instance, we'll allow it, but if the
           * instances differ, throw. */
          Object currentInstance = instance;
          if (currentInstance != UNINITIALIZED && currentInstance != result) {
            throw new IllegalStateException("Scoped provider was invoked recursively returning "
                + "different results: " + currentInstance + " & " + result + ". This is likely "
                + "due to a circular dependency.");
          }
          instance = result;
          /* Null out the reference to the provider. We are never going to need it again, so we
           * can make it eligible for GC. */
          provider = null;
        }
      }
    }
    return (T) result;
  }
  ...
}

我们可以看到由于@MyScrope修饰了A的get方法,因此在DaggerMain2Component中,getA返回的不是Main2Module提供的provideA方法,而是在创建对象的时候,持有了一个Provider的对象,而其本身也是调用了DoubleCheck来产生的一个provider对象,我们看到当调用Provider方法的get方法,是一个单例的写法,也就是说如果之前有了唯一的A,继续调用Provider的get方法并不会产生一个新的A的实例,而是返回A的单例对象。这也就是说一个生成的DaggerXXXComponent持有了一个DoubleCheck实例,而后者又持有需注入依赖的实例,因此实例的生命周期就和Component 一致了。这就是说,如果我们要使用@Scrope注解来进行修饰,注意潜在的内存泄漏的风险。 而对于@Singleton 是Dagger提供的一个默认实现的@Scrope注解。本质上和我们自己定义的@MyScope 没什么区别。

@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}

@Subcomponent 与 @Component.dependencies

这两个注解(后一个并不是真正意义上的注解,姑且这么叫吧)比较容易让人搞混,因为从功能上二者都是为了解决依赖复用情况。其意义上更接近OOP的is-a has-a的关系,我们先将代码贴出来再分析二者的不同。 假设有个 Daddy 类,其注入依赖 Car 类。在Activity中来进行注入。

public class Car {
  public String id;
}

public class Daddy implements inter {
  @Inject Car mCar;

  @Override public void print() {
    Log.i(ARIRUS, "print: "+mCar.id);
  }
}

@Component(modules = {DaddyModule.class})
public interface DaddyComponent {
  Daddy inject(Daddy daddy);
}

@Module
public class DaddyModule {
  @Provides Car getCar(){
    Car car = new Car();
    car.id= getClass().getName();
    return car;
  }
}

public class Main2Activity extends AppCompatActivity {

  @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main2);

    Daddy daddy = new Daddy();

    DaddyComponent daddyComponent = DaggerDaddyComponent.create();
    daddyComponent.inject(daddy);

    daddy.print();
  }
}
//打印出
I/Main2Activity: print: com.arirus.myapplication.DaddyModule

@Component.dependencies

现在一个 Mam 类,其内部也有一个 Car ,定义如下

public class Mam implements inter{
  @Inject Car mCar;

  @Override public void print() {
    Log.i(ARIRUS, "print: "+mCar.id);
  }
}

但是如果Component还是像DaddyComponent一样,就要自己定义Module 来 @Provides Car 实例,如果我们想从 DaddyComponent 直接获取,即Car的实例,只从 DaddyComponent 来获取我们可以定义MamComponent如下:

@Component(dependencies = DaddyComponent.class)
public interface MamComponent {
  Mam inject(Mam mam);
}

使用Component.dependencies 来指定依赖的 Component 。同时在 DaddyComponent 要做相应的修改:

@Component(modules = {DaddyModule.class})
public interface DaddyComponent {
  Daddy inject(Daddy daddy);

  Car getCar();
}

添加 Car 的 Provider 方法。这样在inject(mam) 时,Car 的实例便由 DaddyComponent 来提供。 最后就是在Activity中的调用:

Daddy daddy = new Daddy();
Mam mam = new Mam();

DaddyComponent daddyComponent = DaggerDaddyComponent.create();
daddyComponent.inject(daddy);

DaggerMamComponent.builder().daddyComponent(daddyComponent).build().inject(mam);

所以,调用的时候是将一个 DaddyComponent 实例,注入到 DaggerMamComponent ,以便使用其的 Car 对象。 我们来看下几个关键的编译后的文件

//DaggerDaddyComponent
public final class DaggerDaddyComponent implements DaddyComponent {
  private DaddyModule daddyModule;
  ...
  @Override
  public Daddy inject(Daddy daddy) {
    return injectDaddy(daddy);
  }

  @Override
  public Car getCar() {
    return DaddyModule_GetCarFactory.proxyGetCar(daddyModule);
  }

  private Daddy injectDaddy(Daddy instance) {
    Daddy_MembersInjector.injectMCar(instance, DaddyModule_GetCarFactory.proxyGetCar(daddyModule));
    return instance;
  }

  public static final class Builder {
    private DaddyModule daddyModule;

    public DaddyComponent build() {
      if (daddyModule == null) {
        this.daddyModule = new DaddyModule();
      }
      return new DaggerDaddyComponent(this);
    }
    ...
  }
}

//DaggerMamComponent
public final class DaggerMamComponent implements MamComponent {
  private DaddyComponent daddyComponent;
  ...
  @Override
  public Mam inject(Mam mam) {
    return injectMam(mam);
  }

  private Mam injectMam(Mam instance) {
    Mam_MembersInjector.injectMCar(
        instance,
        Preconditions.checkNotNull(
            daddyComponent.getCar(), "Cannot return null from a non-@Nullable component method"));
    return instance;
  }

  public static final class Builder {
    private DaddyComponent daddyComponent;

    private Builder() {}

    public MamComponent build() {
      if (daddyComponent == null) {
        throw new IllegalStateException(DaddyComponent.class.getCanonicalName() + " must be set");
      }
      return new DaggerMamComponent(this);
    }

    public Builder daddyComponent(DaddyComponent daddyComponent) {
      this.daddyComponent = Preconditions.checkNotNull(daddyComponent);
      return this;
    }
  }
}

两个 DaggerXXXComponent 文件核心部分如上所示。对于DaggerDaddyComponent inject时,直接使用 DaddyModule 实例提供的 getCar 方法,来获取 Car 依赖,进行注入。对于 DaggerMamComponent ,要动态传入 DaddyComponent,最后在inject的方法中,调用了 DaddyComponent 实现类(DaggerDaddyComponent)来获取的 Car 依赖。

@Subcomponent

加入再有一个Child 类,定义如下:

public class Child implements inter {

  @Inject Car mCar;

  @Override public void print() {
    Log.i(ARIRUS, "print: "+mCar.id);
  }
}

其内部也有一个 Car ,如果和 Mam 类似,其没有相应的Module的Provides 方法来提供 Car 的依赖,但不以dependencies的形式,便可以以@Subcomponent形式来做:

@Subcomponent
public interface ChildComponent {

  void inject(Child child);

  @Subcomponent.Builder
  interface Builder{
    ChildComponent build();
  }
}

@Component(modules = {DaddyModule.class})
public interface DaddyComponent {
  Daddy inject(Daddy daddy);

  Car getCar();

  ChildComponent.Builder childComponent();
}

//注入
Child child = new Child();

DaddyComponent daddyComponent = DaggerDaddyComponent.create();
daddyComponent.childComponent().build().inject(child);

这样Child 便可以使用,Daddy的所有依赖。然后看下编译过后的文件,ChildComponent 没有生成相应的 DaggerChildComponent 文件,所以主要是 DaggerDaddyComponent 文件:

public final class DaggerDaddyComponent implements DaddyComponent {
  private DaddyModule daddyModule;

  private DaggerDaddyComponent(Builder builder) {
    initialize(builder);
  }

  public static Builder builder() {
    return new Builder();
  }

  public static DaddyComponent create() {
    return new Builder().build();
  }

  @SuppressWarnings("unchecked")
  private void initialize(final Builder builder) {
    this.daddyModule = builder.daddyModule;
  }

  @Override
  public Daddy inject(Daddy daddy) {
    return injectDaddy(daddy);
  }

  @Override
  public Car getCar() {
    return DaddyModule_GetCarFactory.proxyGetCar(daddyModule);
  }

  @Override
  public ChildComponent.Builder childComponent() {
    return new ChildComponentBuilder();
  }

  private Daddy injectDaddy(Daddy instance) {
    Daddy_MembersInjector.injectMCar(instance, DaddyModule_GetCarFactory.proxyGetCar(daddyModule));
    return instance;
  }

  public static final class Builder {
    private DaddyModule daddyModule;

    private Builder() {}

    public DaddyComponent build() {
      if (daddyModule == null) {
        this.daddyModule = new DaddyModule();
      }
      return new DaggerDaddyComponent(this);
    }

    public Builder daddyModule(DaddyModule daddyModule) {
      this.daddyModule = Preconditions.checkNotNull(daddyModule);
      return this;
    }
  }

  private final class ChildComponentBuilder implements ChildComponent.Builder {
    @Override
    public ChildComponent build() {
      return new ChildComponentImpl(this);
    }
  }

  private final class ChildComponentImpl implements ChildComponent {
    private ChildComponentImpl(ChildComponentBuilder builder) {}

    @Override
    public void inject(Child child) {
      injectChild(child);
    }

    private Child injectChild(Child instance) {
      Child_MembersInjector.injectMCar(
          instance, DaddyModule_GetCarFactory.proxyGetCar(DaggerDaddyComponent.this.daddyModule));
      return instance;
    }
  }
}

可以看到没有生成 DaggerChildComponent,而是生成了 ChildComponentImpl 以内部类的形式存在于 DaggerDaddyComponent 。同时可以看到,不通过与 DaggerMamComponent 的injectMCar 调用的 daddyComponent.getCar(),而是直接调用的 DaddyModule_GetCarFactory.proxyGetCar(DaggerDaddyComponent.this.daddyModule),因此二者最大的区别就是对于@Component.dependencies,只能使用暴露出的接口来进行注入,而用@SubComponent 则是完全继承,无论是否暴露出接口,只要在Module的中定义了相应的Provider,就可以直接进行注入。

总结

本篇其实是比较几个在Dagger中常用注解的异同,算是对于上篇的一个补充,下一篇说说在开发中的具体应用。

results matching ""

    No results matching ""