解析Dagger中的Scope

作者: 工程师milter | 来源:发表于2016-09-12 10:48 被阅读1466次

    本文适合学习使用过Dagger后,对Scope概念还感到困惑的朋友,文中不会介绍Dagger的基本知识。

    Dagger是一个优秀的依赖注入框架,使用它会大大降低项目各模块间的耦合,提高项目的可扩展性。我看到简书上讲Dagger的文章不少,但大都对其Scope概念语焉不详,显然作者本人也没有完全搞清楚。于是我决定写篇小文来解释一下Scope的含义及用法。

    一、不同组件有不同的用途

    使用Dagger,需要定义很多组件,不同的组件是为了完成不同的依赖注入功能。比如,有的组件是为了给某个Activity注入依赖,有的是为了给某个Service注入依赖。从组件里面的inject方法可以了解这个组件是给谁注入依赖的。举例如下:

    //这个组件是用来给MainActivity注入依赖的
    @Component(modules ={MainModule.class})
    public interface MainComponent {
      void inject(MainActivity activity);
    }
    
    //这个组件是用来给MainService注入依赖的
    @Component(modules ={BackModule.class})
    public interface BackComponent {
      void inject(MainService service);
    }
    

    二、让MainComponent与MainActivity的生命周期保持同步

    MainComponent用来给MainActivity注入依赖,我们当然希望MainComponent与MainActivity“同生共死”,即MainActivity创建的同时创建MainComponent,MainActivity销毁的同时销毁MainComponent。

    那么怎么做到呢?

    1、定义Scope

    我们可以定义一个Scope注解,把它加到MainComponent头上。

    定义Scope如下:

    @Scope
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MainActivityScope {
    
    }
    

    2、为MainComponent添加Scope

    现在,我们把上面自定义的Scope添加到MainComponent头上,如下:

    //这个组件是用来给MainActivity注入依赖的
    @MainActivityScope
    @Component(modules ={MainModule.class})
    public interface MainComponent {
      void inject(MainActivity activity);
    }
    

    3、将MainComponent与MainActivity同步

    很多初学者,误以为添加上自定义的Scope后,MainComponent就会获得一种神奇的能力,能够自动实现与MainActivity的“生死与共”。这种想法天真烂漫可爱,但却是错误的。

    添加上自定义的Scope后,MainComponent并没有获得什么超能力,要想让它与MainActivity的生命周期同步,还需要我们手动完成,即:

    • MainActivity的onCreate方法中创建MainComponent并完成依赖注入
    • MainActivity的onDestroy方法中销毁MainComponent

    三、Scope为我们带来的好处

    有的人说,既然Scope根本无法决定MainComponent的创建销毁,最终还要靠我们手动完成,那我为什么要使用Scope?

    原因有三。

    1、方便管理

    在MainComponent加上@MainActivityScope注解后,我们可以很清楚地知道,这个组件是用来给MainActivity注入依赖的,这会提醒我们将MainComponent的创建销毁与MainActivity关联起来。

    2、不得不用

    Dagger可以使用组件依赖,将不同的组件组合起来完成某个依赖注入任务,但它规定:父组件与子组件不能有相同的Scope,这时候就不得不自己定义Scope。

    3、实现单例

    这是我认为最重要的一点。还是以上面的MainComponent为例,它有一个模块,即MainModule,假设它包含如下内容:

    @Module
    public class MainModule {
        Application mApp;
        public MainModule(Application app){
            this.mApp = app ;
        }
        @Provides
        Application providesApplication(){
                  return mApp ;
        }
        @Provides
        OkHttpClient providesOkHttpClient(){
              return new OkHttpClient();
        }
    

    Dagger规定,一个组件的模块中的@Provides注解的方法,要不加上与组件相同的Scope,要不就不加。在我们的例子中,MainModule的两个被@Provides注解的方法都没有加@MainActivityScope注解。我们来看看这会造成什么后果。

    第一个方法提供Application实例,它直接返回模块MainModule内缓存的Application实例,由此我们知道,组件MainComponent提供的Application实例只有一个,就是模块MainModule创建时传入的Application实例,这时我们说,MainComponent注入的Application实例是一个单例

    第二个方法提供OkHttpClient实例,在 providesOkHttpClient方法中,会new一个OkHtttpClient实例返回。这意味着,每当我们用MainComponent注入OkHttpClient实例时,都会重新创建一个崭新的OkHttpClient实例,如果你注入五次,那么就会有五个OkHttpClient实例。

    这显然不是我们想要的结果,我们希望用MainComponent注入的OkHttpClient也是单例,怎么办?
    我们可以给providesOkHttpClient方法加上@MainActivityScope注解,如下:

     @Provides
     @MainActivityScope
        OkHttpClient providesOkHttpClient(){
              return new OkHttpClient();
        }
    

    有了这个注解,当我们第一次用MainComponent注入OkHttpClient实例时,Dagger会调用providesOkHttpClient方法创建一个OkHttpClient实例返回,同时,Dagger会缓存这个新创建的实例。

    以后,只要这个MainComponent没有销毁,再次使用MainComponent注入OkHttpClient实例时,Dagger都会直接返回缓存的OkHttpClient实例,而不是调用providesOkHttpClient方法创建新的OkHttpClient实例。这就确保了MainComponent注入的依赖都是一个实例,也就是实现了单例模式。

    好了,希望你读完本文能够帮助你理解掌握Dagger中的Scope概念,在工作中更好地使用Dagger。

    相关文章

      网友评论

      • 天梦5213:实用,通俗易懂
      • 疯狂小磊哥:你好,讲的挺实用,但是个人感觉还不是很详细,或者给个Demo。MainActivitScope必须前缀是MainActivity后缀是Scope才是和MainActivity有关系吗?命名规则是什么?我看还有 @Scope
        @Retention(RUNTIME)
        public @interface PerActivity {}
      • tmp_zhao:实现单例的话,貌似直接@Singleton就行了。。。
        c1cc510ed79a:你想多了,亲.
        单例的实现需要保证几点
        1.同一个component对象
        2.和该component拥有同样scope的对象(也就是你@provides注解的方法所返回的对象)
      • Vander丶:这才是 真正的解决问题的文章 .
        Vander丶:深度 好文. 我其实 就是开始以为 Dagger2存在某种神奇的功能.
        工程师milter: @Vander丶 你很有眼光!可惜这样的文章阅读量一直上不去
      • Jude95:请问自定义的 @MainActivityScope 与 @singleton 的作用有什么区别呢。
        0e448f4271f5:@Jude95 我之后知道了,之所以混乱是因为网上各种文章瞎写,我都不敢看了
        Jude95:@Joash 没有区别。只是给开发者看的。让开发者起个名以时刻提醒自己这个单例的作用域是在activity存在的时域内...
        0e448f4271f5:这个问题解决了吗?看了不少文章,总感觉有问题,目前推测@Singleton是基于Component的,不同的地方创建Component不同导致这些地方就算有相同的对象并且@Singleton过,仍然也不能单例,但是@Scope可以
      • HalZhang:还是没明白scope的作用。 :smile:
        怜悯是我的座右铭:我也是,看完了也还是糊里糊涂的:anguished:
        HalZhang:@milter 多用用,多踩点坑,哈哈
        工程师milter: @HalZhang 这就尴尬了😅
      • WiiHuu:MainActivity的onDestroy方法中销毁MainComponent

        ----
        请问这个怎么销毁
        工程师milter: @WiiHuu 将component设为null
      • 68768b474bfc:这确实是dagger里面一个难点:smirk:
        工程师milter: @TellH 是的,很多人都搞不清楚。欢迎各种方式推广这篇文章,造福其他人哈
      • b729533a3e16:板凳板凳 :+1:
        @Scope 加到 module上是不是没有神马用?
        工程师milter: @李小Bo 那就对了
        b729533a3e16:@milter 木有用 :flushed:
        工程师milter: @李小Bo 木有用,你也可以自己验证下
      • 工程师milter:自己评论自己的文章也很好玩哈
        工程师milter:@small_tel 港真,你可以试试:smile:
        339be3b8c34f:真的吗,真的好玩吗,不要骗我

      本文标题:解析Dagger中的Scope

      本文链接:https://www.haomeiwen.com/subject/czlpettx.html