Dagger 2学习与探索(六)

上一期稍微探究了一下交叉依赖,这一期来探究一下Dagger里的@Scope标注。
Scope是范围的意思,Dagger里面自带了一个已经可以用的范围标注:@Singleton
我们知道,Singleton是单例的意思,那么这里的单例标注是何含义?
先看看@Singleton的代码:

/**
 * Identifies a type that the injector only instantiates once. Not inherited.
 *
 * @see javax.inject.Scope @Scope
 */
@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}

注释说,标识一个类型,让注入器只初始化它一次。关于类型,我们不可避免联想到@Provides标注,也是只关注返回类型的。
再看看@Scope标注:

/**
 * Identifies scope annotations. A scope annotation applies to a class
 * containing an injectable constructor and governs how the injector reuses
 * instances of the type. By default, if no scope annotation is present, the
 * injector creates an instance (by injecting the type's constructor), uses
 * the instance for one injection, and then forgets it. If a scope annotation
 * is present, the injector may retain the instance for possible reuse in a
 * later injection. If multiple threads can access a scoped instance, its
 * implementation should be thread safe. The implementation of the scope
 * itself is left up to the injector.
 * 省略中间注释……
 *
 * <p>Annotating scope annotations with {@code @Scope} helps the injector
 * detect the case where a programmer used the scope annotation on a class but
 * forgot to configure the scope in the injector. A conservative injector
 * would generate an error rather than not apply a scope.
 *
 * @see javax.inject.Singleton @Singleton
 */
@Target(ANNOTATION_TYPE)
@Retention(RUNTIME)
@Documented
public @interface Scope {}

这次注释还比较有用,或者说是能看懂的。大意就是,如果没有@Scope标注,注入器每次都会产生新的实例来注入,反之就会保存然后复用。另外还提到了线程安全,不过这些都是Dagger包揽的。
到目前为止,可能你还有不少疑问,不过先来实验一下,或许疑问就迎刃而解了。

主体代码

现在新建一个MyApp继承Application,并在Manifest文件里面声明:

public class MyApp extends Application {

  @Override
  public void onCreate() {
    super.onCreate();
  }
}
  <application
    android:name=".MyApp"
...

好了,现在我们想要做的事情就是,在MyAppMainActivity里面注入两个ClassB,来观察是否真的是单例模式。当然,这仅仅是实验,讲道理单例一般是不带构造参数的。
另外还有一个比较常见的Dagger的应用方法,就是只创建一次Component然后到处传,好处就是复用,然后实际上@Singleton标注也局限于同一个注入器。一般而言创建的场合就是在Application里面了。
首先将ComponentB添加对MyApp@Inject标注方法,并添加@Singleton标注:

@Singleton
@Component(modules = ModuleB.class)
public interface ClassBComponent {

  void inject(MainActivity mainActivity);
  void inject(MyApp myApp);
}

为什么Component也要加@Singleton标注呢?这是Dagger的规定,假如@Provides里面有想要加单例标注的,那么其Component也得加,或许与生成代码的机制有关,我们遵循即可,到后面会有更多说明。
然后对ModuleB里面的对应的ClassB的提供函数加上单例标注:

@Module
public class ModuleB {

  private int a;
  private int b;

  public ModuleB(int a, int b) {
    this.a = a;
    this.b = b;
  }

  @Provides
  @Named("a")
  int provideIntA() {
    return a;
  }

  @Provides
  @Named("b")
  int provideIntB() {
    return b;
  }

  @Provides
  ClassA provideClassA(@Named("a") int a, @Named("b") int b) {
    return new ClassA(a, b);
  }

  @Singleton
  @Provides
  ClassB provideClassB(ClassA classA, @Named("a") int a) {
    return new ClassB(classA, a);
  }
}

为方便看出ClassB的构造,在ClassB的构造函数里加一个Log:

public class ClassB {
  private static final String TAG = "ClassB";
  private ClassA classA;
  private int a;

  public ClassB(ClassA classA, int a) {
    this.classA = classA;
    this.a = a;
    Log.d(TAG, "classB constructor called");
  }

  public ClassA getClassA() {
    return classA;
  }

  public int getA() {
    return a;
  }
}

现在对MyApp做一些变更:

public class MyApp extends Application {

  private static MyApp appInstance = null;
  private static ClassBComponent classBComponent = null;

  @Inject ClassB classB;

  @Override
  public void onCreate() {
    super.onCreate();

    appInstance = this;
    classBComponent = DaggerClassBComponent.builder().moduleB(new ModuleB(2, 3)).build();
    classBComponent.inject(this);
  }

  public static MyApp getAppInstance() {
    return appInstance;
  }

  public static ClassBComponent getClassBComponent() {
    return classBComponent;
  }
}

然后再是MainActivity

public class MainActivity extends AppCompatActivity {
  @Inject ClassB classB;

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

    MyApp.getClassBComponent().inject(this);
  }
}

运行发现构造函数确实只被调用了一次。假如把两个@Singleton去掉可以发现调用了两次,有兴趣的可以自己试一试。
仔细想想,也许没那么神奇?毕竟是对同一个Component而言,既然刚开始就获得了所有需要的参数,生成一些单例代码也不那么奇怪。

生成代码

生成代码变化不如想象中那么大。首先四个工厂还是雷打不动,说明单例的实现并没有落在ClassB的工厂类上面;然后多了一个MyApp_MembersInjector原理和MainActivity_MembersInjector类似。
真正实现单例的是DaggerClassBComponent,这也解释了为什么要在其上添加@Singleton标注:

public final class DaggerClassBComponent implements ClassBComponent {
  private Provider<Integer> provideIntAProvider;

  private Provider<Integer> provideIntBProvider;

  private Provider<ClassA> provideClassAProvider;

  private Provider<ClassB> provideClassBProvider;

  private MembersInjector<MainActivity> mainActivityMembersInjector;

  private MembersInjector<MyApp> myAppMembersInjector;

  private DaggerClassBComponent(Builder builder) {
    assert builder != null;
    initialize(builder);
  }

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

  @SuppressWarnings("unchecked")
  private void initialize(final Builder builder) {

    this.provideIntAProvider = ModuleB_ProvideIntAFactory.create(builder.moduleB);

    this.provideIntBProvider = ModuleB_ProvideIntBFactory.create(builder.moduleB);

    this.provideClassAProvider =
        ModuleB_ProvideClassAFactory.create(
            builder.moduleB, provideIntAProvider, provideIntBProvider);

    this.provideClassBProvider =
        DoubleCheck.provider(
            ModuleB_ProvideClassBFactory.create(
                builder.moduleB, provideClassAProvider, provideIntAProvider));

    this.mainActivityMembersInjector = MainActivity_MembersInjector.create(provideClassBProvider);

    this.myAppMembersInjector = MyApp_MembersInjector.create(provideClassBProvider);
  }

  @Override
  public void inject(MainActivity mainActivity) {
    mainActivityMembersInjector.injectMembers(mainActivity);
  }

  @Override
  public void inject(MyApp myApp) {
    myAppMembersInjector.injectMembers(myApp);
  }

  public static final class Builder {
    private ModuleB moduleB;

    private Builder() {}

    public ClassBComponent build() {
      if (moduleB == null) {
        throw new IllegalStateException(ModuleB.class.getCanonicalName() + " must be set");
      }
      return new DaggerClassBComponent(this);
    }

    public Builder moduleB(ModuleB moduleB) {
      this.moduleB = Preconditions.checkNotNull(moduleB);
      return this;
    }
  }
}

核心代码是这里:

 this.provideClassBProvider =
        DoubleCheck.provider(
            ModuleB_ProvideClassBFactory.create(
                builder.moduleB, provideClassAProvider, provideIntAProvider));

那么DoubleCheck又是什么呢?

/**
 * A {@link Lazy} and {@link Provider} implementation that memoizes the value returned from a
 * delegate using the double-check idiom described in Item 71 of <i>Effective Java 2</i>.
 */
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;
  }

  /** Returns a {@link Provider} that caches the value from the given delegate provider. */
  public static <T> Provider<T> provider(Provider<T> delegate) {
    checkNotNull(delegate);
    if (delegate instanceof DoubleCheck) {
      /* This should be a rare case, but if we have a scoped @Binds that delegates to a scoped
       * binding, we shouldn't cache the value again. */
      return delegate;
    }
    return new DoubleCheck<T>(delegate);
  }

  /** Returns a {@link Lazy} that caches the value from the given provider. */
  public static <T> Lazy<T> lazy(Provider<T> provider) {
    if (provider instanceof Lazy) {
      @SuppressWarnings("unchecked")
      final Lazy<T> lazy = (Lazy<T>) provider;
      // Avoids memoizing a value that is already memoized.
      // NOTE: There is a pathological case where Provider<P> may implement Lazy<L>, but P and L
      // are different types using covariant return on get(). Right now this is used with
      // DoubleCheck<T> exclusively, which is implemented such that P and L are always
      // the same, so it will be fine for that case.
      return lazy;
    }
    return new DoubleCheck<T>(checkNotNull(provider));
  }
}

其原理来自于Double Check的单例模式,这里就是把传入的工厂再包一层单例,至于细节有兴趣的可以自行研究。

看Dagger的实现好像也不是那么难不是吗?其实Dagger还支持自定义@Scope,只需要仿照@Singleton的样子改个名字就行了,但其实Dagger会把它当做@Singleton来对待,或者说Dagger其实压根只认识@Scope
你可能会觉得很疑惑。不过想一想,随便加一个@PerActivity之类的就让Dagger掌控Activity的生命周期,然后做出相应调整,那是童话故事。事实就是,Dagger只认@Scope,只当做单例处理,这也是为什么Dagger只带了这么一个标注,而且改名叫这个。
那么是不是自定义@Scope就没有意义了呢?也不尽然。好的名字能直接起到注释的作用,比如@PerActivity就时刻提醒作者和读者这个Component生命周期应该局限在Activity之内。也就是说,其实最后真正要实现PerActivity的还是使用的人。
学习了@Scope,下一期我们将探究Component dependencySubcomponent

    原文作者:akak18183
    原文地址: https://www.jianshu.com/p/676550953430
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞