Dagger2 Scope 如何实现局部单例

Dagger2

关于Dagger2的一些基础概念(Inject,Component,Module,Provides)及注入原理,在网上也非常多关于这方面的文章,这里我就不多讲,这里推荐几篇文章,大家可以先仔细看看

dagger2让你爱不释手-基础依赖注入框架篇

dagger2让你爱不释手-重点概念讲解、融合篇

dagger2让你爱不释手-终结篇

dagger2注入原理

Scope

Scope是Dagger2中的作用域,在上面的文章也详细的介绍了关于的它的作用,主要是

  • 更好的管理Component之间的组织方式,不管是依赖方式还是包含方式,都有必要用自定义的Scope注解标注这些Component,这些注解最好不要一样了,不一样是为了能更好的体现出Component之间的组织方式。还有编译器检查有依赖关系或包含关系的Component,若发现有Component没有用自定义Scope注解标注,则会报错。
  • 更好的管理Component与Module之间的匹配关系,编译器会检查 Component管理的Modules,若发现标注Component的自定义Scope注解与Modules中的标注创建类实例方法的注解不一样,就会报错。
  • 可读性提高,如用Singleton标注全局类,这样让程序猿立马就能明白这类是全局单例类。

但是还有个很重要的作用上面的没有讲解到,那就是关于Scope可以实现局部单例

Scope实现局部单例

这里的示例我引用了Piasy关于Scope的文章中的例子,通过这个例子,我们可以得出以下结论:

  • component 的 inject 函数不要声明基类参数;
  • Scope 注解必须用在 module 的 provide 方法上,否则并不能达到局部单例的效果;
  • 如果 module 的 provide 方法使用了 scope 注解,那么 component 就必须使用同一个注解,否则编译会失败;
  • 如果 module 的 provide 方法没有使用 scope 注解,那么 component 和 module 是否加注解都无关紧要,可以通过编译,但是没有局部单例效果;
  • 对于直接使用 @Inject 构造函数的依赖,如果把 scope 注解放到它的类上,而不是构造函数上,就能达到局部单例的效果了;

我们来测试下Module的Provider方法是否使用@ActivityScope标注,apt给我们生成的源代码

DaggerDemoComponent.java

使用@ActivityScope标注

1
this.demoDirectInjectDependencyProvider=ScopedProvider.create(DemoDirectInjectDependency_Factory.create(provideContextProvider));

不使用@ActivityScope标注

1
this.demoInjectDependencyImplProvider=DemoInjectDependencyImpl_Factory.create(provideContextProvider);

可以看出他们得到Provider的方式是不一样的,使用@ActivityScope注解的是通过ScopedProvider拿到的,我们看下

ScopedProvider.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public final class ScopedProvider<T> implements Provider<T> {
private volatile Object instance = UNINITIALIZED;
//...
private ScopedProvider(Factory<T> 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;
}
//...
}

看到这里想必大家已经明白,ScopedProvider返回给我们的是一个单例,这个单例是依赖于DemoComponent的,所以叫它局部单例。

其实从这点也能理解为什么叫它作用域的原因了,因为它的创建和销毁跟Component是息息相关的,也印证了上面提到管理作用

参考

dagger2让你爱不释手-基础依赖注入框架篇

dagger2让你爱不释手-重点概念讲解、融合篇

dagger2让你爱不释手-终结篇

dagger2注入原理

Dagger2 Scope 注解能保证依赖在 component 生命周期内的单例性吗?