转载请标明出处:http://blog.csdn.net/xx326664162/article/details/67640509 文章出自:薛瑄的博客
Dagger2 系列:
Dagger2 入门详解(一)
@Scope 看这一篇就够了——Dagger2 (二)
scope 字面意思是范围,标志一个注入器/对象的使用范围,很多文章说成生命周期也是可以的。比如,单例模式,局部单例模式等等。
在Dagger 2中
1、@Singleton可以保持类的单例。
2、@ApplicationScope注解的Component类与Applicaiton对象的生命周期一致。
3、@ActivityScope注解的Component类与Activity的生命周期一致
scope可以给我们带来“局部单例”,生命周期取决于scope自己。
但是需要弄清楚的是 - Dagger 2默认并不提供@ActivityScope 、 @ApplicationScope 这些注解。这些只是最常用的自定义Scope。只有@Singleton scope是默认提供的(由Java自己提供)。
使用也很简单,比如使用@Singleton 。
@Module
class MachineModule{
@Singleton //1.添加@Singleton标明该方法产生只产生一个实例
@Provides
Machine provideFruitJuiceMachine(){
return new FruitJuiceMachine();
}
}
@Singleton //2.添加@Singleton标明该Component中有Module使用了@Singleton
@Component(modules=MachineModule.class)
class JuiceComponent{
void inject(Container container)
}
public class Test{
public static void main(String... args){
JuiceComponent c1=DaggerJuiceComponent.create();
c1.inject(container1);
c1.inject(container2);
//由于制造machine的方法使用了@Singleton,所以先后注入container1,container2中的machine相同
System.out.println(conainter1.machine==conainter2.machine);//true
}
}
上面的例子可以看到,实现单例需要两步 :
为了更好地去理解Dagger 2中的scope,我们直接进入实践案例。我们将要去实现比Application/Activity scope更加复杂一点的scope。假如现在需要三种scope:
@Sigleton - application scope 全局的单例对象
@UserScope - 用于与当前用户联系起来的类实例的scope(在真实的app中可以是当前登录的用户)。
@ActivityScope - 生命周期与Activity(在我们例子中的呈现者)一致的实例的scope
请注意@Sigleton可以保证是单例,是dagger2内部控制的。(主要代码详见dagger2中dagger-2.2-sources.jar!\dagger\internal\ScopedProvider.java中的get()函数)
@UserScope,@ActivityScope 控制实例的生命周期,需要程序员自己去控制对应的Component。
使用方法参数获取用户数据的类(在我们的例子中是RepositoriesManager),我们可以通过构造参数(它将通过依赖图表提供)的方式来获取User实例并在需要的时候被初始化,而不是在app启动的时候创建它。这意味着RepositoriesManager可以在我们从Github API获取到用户信息(在RepositoriesListActivity呈现之前)之后被创建。
这里有个我们app中scopes和components呈现的简单图表。
单例(Application scope)是最长的scope(在实践中是与application一样长)。
UserScope作为Application scope的一个子集scope,它可以访问它的对象(我们可以从父scope中得到对象)。
ActivityScope(生命周期与Activity一致)也是如此 - 它可以从UserScope和ApplicationScope中得到对象。
这里有一个我们app中scope生命周期的案例:
单例的生命周期是从app启动后的所有时期,当我们从Github API(在真实app中是用户登录之后)得到了User实例时UserScope被创建了,然后当我们回退到SplashActivity(在真实app中是用户退出之后)时被销毁。
demo地址:https://github.com/frogermcs/GithubClient
@Singleton就是一种Scope注解,也是Dagger2唯一自带的Scope注解,使用它就可以实现单例模式。
这篇文章解锁Dagger2使用姿势(二) 之带你理解@Scope用代码证明了,使用@Singleton就可以实现单例模式。demo:https://github.com/JantHsueh/Dagger2
下面是@Singleton的源码
@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton{}
可以看到定义一个Scope注解,必须添加以下三部分:
@Scope :注明是Scope
@Documented :标记在文档
@Retention(RUNTIME) :运行时级别
Java中,单例通常保存在一个静态域中,这样的单例往往要等到虚拟机关闭时候,该单例所占用的资源才释放。但是,Dagger通过Singleton创建出来的单例并不保持在静态域上,而是保留在Component实例中。要理解这一点,请看下面代码,续上文中的例子
public class Test{
public static void main(String... args){
//c1,c2是不同对象,它们各自缓存machine
JuiceComponent c1=DaggerJuiceComponent.create();
JuiceComponent c2=DaggerJuiceComponent.create();
c1.inject(container1);
c1.inject(container2);
c2.inject(container3);
c2.inject(container4);
System.out.println(conainter1.machine==conainter2.machine);//true
System.out.println(conainter2.machine==conainter3.machine);//false
System.out.println(conainter3.machine==conainter4.machine);//true
System.out.println(conainter4.machine==conainter1.machine);//false
}
}
c1前后两次分别注入container1,container2,每个Component对象保留各自的单例对象,
而container1,container2都是使用c1来注入machine,所以他们的machine对象是相同的。
而container2、container3分别使用c1,c2来注入machine,所以他们的machine对象是不同的。
对于Android,我们通常会定义一个针对整个APP全生命周期的@PerApp的Scope注解,通过仿照@Singleton
@Scope
@Documented
@Retention(RUNTIME)
public @interface PerApp{}
你可能会发现,这个自定义的@Scope 和@Singleton代码完全一样,那@PerApp是不是也能具有实现单例模式的功能?
答案是肯定的
那你可能会有疑问,既然功能都是一样的,干嘛还自定义@Scope ,回忆一下上面第三点使用场景,你会明白,不同的@Scope ,定义单例对象的生命周期,也就是使用范围。在写代码时,程序员更加清楚什么时候创建Component,什么时候结束。
总结起来有以下两点好处:
更好的管理ApplicationComponent和Module之间的关系,Component和Component之间的依赖和继承关系。如果关系不匹配,在编译期间会报错,详解第6点。
代码可读性,让程序猿更好的了解Module中创建的类实例的使用范围。
1、@Component关联的@Module中的任何一个@Provides有@scope,则该整个@Component要加上这个scope。否则在暴露或者注入时(不暴露且不注入时,既不使用它构造对象时,不报错),会有如下错误
Error:(13, 1) 错误: cn.xuexuan.newui.di.component.ActivityComponent (unscoped) may not reference scoped bindings:
@Singleton @Provides android.app.Activity cn.xuexuan.newui.di.module.ActivityModule.getActivity()
2、@Component的dependencies与@Component自身的scope不能相同,即组件之间的scope不能相同,否则出现下面错误。
3、@Singleton的组件不能依赖其他scope的组件,但是其他scope的组件可以依赖@Singleton组件。否则出现下面错误。
4、没有scope的不能依赖有scope的组件。否则出现下面错误。
Error:(21, 1) 错误: com.android.example.devsummit.archdemo.di.component.MyTestComponent (unscoped) cannot depend on scoped components:
@com.android.example.devsummit.archdemo.di.scope.ActivityScope com.android.example.devsummit.archdemo.di.component.MyTestComponentX
5、一个component不能同时有多个scope(Subcomponent除外),否则出现下面的错误
Error:Execution failed for task ‘:app:compileDebugJavaWithJavac’.
java.lang.IllegalArgumentException: com.android.example.devsummit.archdemo.di.component.MyTestComponent was annotated with more than one @Scope annotation
最后奉上我所使用的测试demo:
https://github.com/JantHsueh/Dagger2
https://github.com/frogermcs/GithubClient
参考:
Android注入框架Dagger2学习笔记
Android:dagger2让你爱不释手-重点概念讲解、融合篇
解锁Dagger2使用姿势(二) 之带你理解@Scope
Android常用开源工具(2)-Dagger2进阶
使用Dagger 2依赖注入 - 自定义Scope