Dagger2的使用与理解(3)

这篇我说一下@Scope注解,这个注解说实话我没搞懂,看了很多博客然后自己在写Demo总结,博客里都说就是和依赖的生命周期有关。其实在我看来可以简单地理解为是否与Component绑定,如果与Component绑定,那么Component每次注入的同一个类型的依赖都是相同的,也就是实现局部单例,凡是同一个Component注入的相同依赖都是同一个,如果没有绑定,则每次注入同一个类型的依赖都是重新生成的(new 出来的)。具体看下面代码:

//首先看一下Scope注解,它是一个修饰注解的注解
@Target(ANNOTATION_TYPE)
@Retention(RUNTIME)
@Documented
public @interface Scope {}

//我们新建一个注解,名字取名为ActivityScope 
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityScope {
}

ActivityScope注解一般只在Module和Component上起作用

//我们用@ActivityScope修饰obtain()这个方法,
@Module
public class ActivityModule {
    @Provides
    @ActivityScope
    public String obtain() {
        return new String("123");
    }
}

//@ActivityScope修饰ActivityComponent,这样就把ActivityModule里面的String类型的依赖与ActivityComponent的生命周期绑定在一起了,这里注意一定要两边都使用@ActivityScope,否则编译会报错。
@ActivityScope
@Component(modules = ActivityModule.class)
public interface ActivityComponent {
    void inject(DaggerActivity daggerActivity);
}

//这里我们分别注入两个String类型的对象,观察sss1 和 sss2
public class DaggerActivity extends AppCompatActivity {
    @Inject
    String sss1;

    @Inject
    String sss2;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_gui_view);
        DaggerActivityComponent.create().inject(this);
        Toast.makeText(this, String.valueOf(sss1 == sss2), Toast.LENGTH_SHORT).show();
    }
}

在上面例子中,打出Toast为true,说明sss1和sss2是同一个对象,说明了String类型的依赖和ActivityComponent 注入器绑定在一起,每次注入都使用同一个String注入,而不是重新构造一个。

//从生成的代码分析 obtainProvider的创建在外围又加了一层ScopedProvider。
public final class DaggerActivityComponent implements ActivityComponent {
  private Provider obtainProvider;
  private MembersInjector daggerActivityMembersInjector;
  ...
  ...

  private void initialize(final Builder builder) {  
    this.obtainProvider = ScopedProvider.create(ActivityModule_ObtainFactory.create(builder.activityModule));
    this.daggerActivityMembersInjector = DaggerActivity_MembersInjector.create((MembersInjector) MembersInjectors.noOp(), obtainProvider);
  }
...
...
}

这里是ScopedProvider的实现,它接收一个Factory对象,也就是上面传入的ActivityModule_ObtainFactory.create()的对象,注意,这个类用了一个instance 对象去保存我们的依赖。
从get()方法中可以看出每次去注入依赖的时候,首先将instance赋值给result,然后去判断这个result是否等于初始值,如果等于初始值说明是第一次注入,这边需要同步一下防止多个线程进入破坏单例模式,如果不是第一次注入那么直接返回result,也就是我们保存在instance里面的依赖值。

public final class ScopedProvider<T> implements Provider<T> {
  private static final Object UNINITIALIZED = new Object();

  private final Factory factory;
  private volatile Object instance = UNINITIALIZED;

  private ScopedProvider(Factory factory) {
    assert factory != null;
    this.factory = factory;
  }

  @SuppressWarnings("unchecked") // cast only happens when result comes from the factory
  @Override
  public T get() {
    // double-check idiom from EJ2: Item 71
    Object result = instance;
    if (result == UNINITIALIZED) {
      synchronized (this) {
        result = instance;
        if (result == UNINITIALIZED) {
          instance = result = factory.get();
        }
      }
    }
    return (T) result;
  }

  /** Returns a new scoped provider for the given factory. */
  public static  Provider create(Factory factory) {
    if (factory == null) {
      throw new NullPointerException();
    }
    return new ScopedProvider(factory);
  }
}

这个有点像装饰器,在provider外围再加上一层保持单例的操作。dagger2还内置了一个@Singleton注解,这个其实本质和上面一下,不信你可将上面的@ActivityScope替换成@Singleton,效果和原理完全一样,那么为什么要有这个注解呢,为了使dagger2在使用中能够更加直观,各个依赖的生命周期更加明显。这里还要注意一个Component只能由一个注解修饰,多个@Scope修饰将会编译报错。

上面说到@Scope只能在同一个Component注入条件下才能实现单例,比如在A,B,C三个Activity下,A和B用同一个Component注入相同类型依赖,C用另外一个Component注入相同类型依赖,这样在A,B两个页面内的依赖是同一个,C页面的依赖是另外一个,这里是否相同最关键要看是否是同一个Component注入。关于这个可以参考这篇博客

可见@SingleTon是无法实现全局的单例模式的,那么我们平时最常用的单例模式怎么实现呢,这里我们可以使用android中一个天生的单例的类——Application,与他相关的注入器ApplicationComponent(要用@Scope修饰,Module也是),把我们需要的单例放在ApplicationModule里面提供出来,然后用ApplicationComponent一次注入,因为Appication只实例化一次,所以Appliaction的成员变量都是单例的,从Application里暴露出接口就可以访问得到单例。在下一篇具体讲下关联各个component,子Component能从父Component获取依赖,这些依赖有的就是上面的单例依赖。
差不多@Scope就这些啦

最后一篇讲讲如何关联各个component。

你可能感兴趣的:(android)