@Scope 看这一篇就够了——Dagger2 (二)

转载请标明出处:http://blog.csdn.net/xx326664162/article/details/67640509 文章出自:薛瑄的博客

Dagger2 系列:

Dagger2 入门详解(一)

@Scope 看这一篇就够了——Dagger2 (二)

1、@Scope 到底有什么作用

scope 字面意思是范围,标志一个注入器/对象的使用范围,很多文章说成生命周期也是可以的。比如,单例模式,局部单例模式等等。

在Dagger 2中

1、@Singleton可以保持类的单例。
2、@ApplicationScope注解的Component类与Applicaiton对象的生命周期一致。
3、@ActivityScope注解的Component类与Activity的生命周期一致

scope可以给我们带来“局部单例”,生命周期取决于scope自己。

但是需要弄清楚的是 - Dagger 2默认并不提供@ActivityScope 、 @ApplicationScope 这些注解。这些只是最常用的自定义Scope。只有@Singleton scope是默认提供的(由Java自己提供)。

2、@Scope 的使用

使用也很简单,比如使用@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
    }
}

上面的例子可以看到,实现单例需要两步 :

  1. 在Module对应的Provides方法标明@Singleton
  2. 同时在Component类标明@Singleton

3、@Scope 的用武之地

为了更好地去理解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呈现的简单图表。

@Scope 看这一篇就够了——Dagger2 (二)_第1张图片

单例(Application scope)是最长的scope(在实践中是与application一样长)。

UserScope作为Application scope的一个子集scope,它可以访问它的对象(我们可以从父scope中得到对象)。

ActivityScope(生命周期与Activity一致)也是如此 - 它可以从UserScope和ApplicationScope中得到对象。

@Scope生命周期的例子

这里有一个我们app中scope生命周期的案例:

@Scope 看这一篇就够了——Dagger2 (二)_第2张图片

单例的生命周期是从app启动后的所有时期,当我们从Github API(在真实app中是用户登录之后)得到了User实例时UserScope被创建了,然后当我们回退到SplashActivity(在真实app中是用户退出之后)时被销毁。

demo地址:https://github.com/frogermcs/GithubClient

4、@Singleton

@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对象是不同的。

5、自定义@Scope

对于Android,我们通常会定义一个针对整个APP全生命周期的@PerApp的Scope注解,通过仿照@Singleton

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

你可能会发现,这个自定义的@Scope 和@Singleton代码完全一样,那@PerApp是不是也能具有实现单例模式的功能?

答案是肯定的

那你可能会有疑问,既然功能都是一样的,干嘛还自定义@Scope ,回忆一下上面第三点使用场景,你会明白,不同的@Scope ,定义单例对象的生命周期,也就是使用范围。在写代码时,程序员更加清楚什么时候创建Component,什么时候结束。

总结起来有以下两点好处:

  • 更好的管理ApplicationComponent和Module之间的关系,Component和Component之间的依赖和继承关系。如果关系不匹配,在编译期间会报错,详解第6点。

  • 代码可读性,让程序猿更好的了解Module中创建的类实例的使用范围。

6、使用@Scope的一些经验

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

你可能感兴趣的:(Dagger,2)