Dagger2教程六之Component的组织方法(原)_daggeractivitycomponent-程序员宅基地

技术标签: Inject  Dagger2  Component  

        为了介绍Dagger2的使用,我们搭建了一个Demo来逐步分析,大家可以在 这里下载源码( 这个源码与之前的五个小节源码不同)(https://github.com/dushaofeng/DaggerDemo2.git)。
        上一节我们介绍了 《Dagger2教程五之单例模式》,这一节我们来介绍Component的组织方法。
        所谓Component组织方法,也就是我们工程中的Component该如何分布和结合。
        对于一款APP来说,一些基础的服务类比如全局Log、图片加载器、网络请求器、缓存器等应该做到全局单例,而对某个Activity或者Fragment来说又有自己的单例或者非单例的对象,那么这种情况下该如何组织我们的注入结构呢?
        我们现在知道Component是连接注入类和目标类的桥梁,那么最简单的结构应该是这样的:
        1、Application负责创建全局的单例或者非单例注入类的Component对象
        2、Activity或Fragment在继承Application提供的Component基础上扩展自己的Component接口
        那么具体该如何操作呢?
        Dagger2给我们提供两种方法来实现注入继承。


一、使用dependencies属性实现继承注入

        如果对比源码看的话, 请将源码分支切换到UseDependencies分支


1.1、准备ApplicationBean对象

        我们创建一个ApplicationBean对象用来作为目标类,准备将其注入到应用中:
        public class ApplicationBean {
            private String name = null;


            public ApplicationBean() {
                name = "AppBean";
            }


            public String getAppBeanName() {
                return name;
            }
        }


1.2、准备APP级别的Module对象

        然后创建ApplicationModule用来将其注入到目标类,并且我们标记了Singleton准备将其作为单例模式注入:
        @Module
        public class ApplicationModule {
            //作为单例模式注入app
            @Singleton
            @Provides
            ApplicationBean privoderAppBean() {
                return new ApplicationBean();
            }
        }


1.3、准备APP级别的Component对象

        相应的,我们创建ApplicationComponent用来连接ApplicationModule和Application:
        @Singleton
        @Component(modules = ApplicationModule.class)
        public interface ApplicationComponent {
            void inject(DaggerApplication application);


            //说明将BeanForApplication开放给其他Component使用
            ApplicationBean providerAppBean();
        }
        在这里请注意两点:
        1、由于我们设计要将ApplicationBean作为单例注入,因此ApplicationComponent也需要标记@Singleton标识
        2、我们在ApplicationComponent中提供了一个返回值为ApplicationBean对象的方法声明, 它的作用是将该Component中的ApplicationBean对象暴露给其他Component使用,相当于AIDL语言中的方法声明


1.4、注入Application

        我们需要在Application中完成两个任务:
        1、将ApplicationBean注入到Application内部
        2、将ApplicationComponent对象共享给Activity或者其他类
        具体实现如下:
        public class DaggerApplication extends Application {
            private ApplicationComponent mAppComponent;
            @Inject
            ApplicationBean mAppBean1;
            @Inject
            ApplicationBean mAppBean2;


            @Override
            public void onCreate() {
                super.onCreate();
                if (mAppComponent == null) {
                    mAppComponent = DaggerApplicationComponent.create();
                }
                mAppComponent.inject(this);
                Log.d("Dagger", "Application mAppBean1:" + mAppBean1);
                Log.d("Dagger", "Application mAppBean2:" + mAppBean2);
            }


            public ApplicationComponent getAppComponent() {
                return mAppComponent;
            }
        }
        在这里我们注入了两次ApplicationBean对象,并在注入完成后打印出它们的地址用于观察是否实现了单例的功能。


1.5、准备ActivityBean对象

        我们再创建一个Activity的Bean对象用于观察注入情况:
        public class ActivityBean {
            private String name = null;


            public ActivityBean() {
            }


            public String getAppBeanName() {
                return name;
            }
        }


1.6、准备Activity的Module对象

        Activity的Module应该提供ActivityBean的注入方式:
        @Module
        public class ActivityModule {
            @Provides
            ActivityBean providerActivityBean() {
                return new ActivityBean();
            }
        }


1.7、准备Activity的Component对象

        我们要在Activity的Component中继承ApplicationComponent,也就是要 让Activity的Component不仅可以从ActivityModule中查找注入类,还要能从ApplicationModule中查找到注入类
        @ForActivity
        @Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class)
        public interface ActivityComponent {
            void inject(MainActivity activity);


            void inject(MainActivity.OtherClass otherClass);
        }
        这个Component的写法有三处与之前的写法不同的地方:
        1、添加了ForActivity的修饰,而这个ForActivity就是我们自定义的Scope的一种,根据之前我们的介绍, 他的作用和Singleton是一样的,用于限制该Component的使用范围:
        @Scope
        @Retention(RUNTIME)
        public @interface ForActivity {
        }
        为什么要添加这个修饰呢?因为当前Component所继承的ApplicationComponent中包含Singleton的注释, 所以ApplicationComponent的子类Component的作用范围不能高于ApplicationComponent的作用范围,因此需要对ActivityComponent也添加Scope的限定。
        2、Component中多了"dependencies = ApplicationComponent.class"的注释,它的作用就是告诉Dagger, 当前Component依赖于ApplicationComponent,在查找注入类的时候不仅要在ActivityModule中查找,还需要去ApplicationComponent中的Module中查找。
        3、我们提供了两个inject()方法,作用是要将该Component同时注入到两个对象中,这在之前的介绍中使用过。


1.8、设计Activity对象

        我们接下来就要在Activity中同时注入ActivityBean和ApplicationBean对象了,并且ApplicationBean还是全局单例的模式,为了扩展测试,我们在Activity中还创建了一个OtherClass,也将ActivityBean和ApplicationComponent都注入进去进行观察:
        public class MainActivity extends AppCompatActivity {
            @Inject
            ApplicationBean applicationBean1;
            @Inject
            ApplicationBean applicationBean2;
            @Inject
            ActivityBean activityBean;


            @Override
            protected void onCreate(@Nullable Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_main);


                DaggerApplication application = (DaggerApplication) getApplication();
                ApplicationComponent applicationComponent = application.getAppComponent();
                ActivityComponent activityComponent = DaggerActivityComponent.builder().applicationComponent(applicationComponent).build();
                activityComponent.inject(this);
                Log.d("Dagger", "Activity activityBean:" + activityBean);
                Log.d("Dagger", "Activity applicationBean1:" + applicationBean1);
                Log.d("Dagger", "Activity applicationBean2:" + applicationBean2);
                OtherClass otherClass = new OtherClass();
            }


            class OtherClass {
                @Inject
                ApplicationBean applicationBean1;
                @Inject
                ApplicationBean applicationBean2;
                @Inject
                ActivityBean activityBean;


                public OtherClass() {
                    DaggerApplication application = (DaggerApplication) getApplication();
                    ApplicationComponent applicationComponent = application.getAppComponent();
                    ActivityComponent activityComponent = DaggerActivityComponent.builder().applicationComponent(applicationComponent).build();
                    activityComponent.inject(this);
                    Log.d("Dagger", "OtherClass activityBean:" + this.activityBean);
                    Log.d("Dagger", "OtherClass applicationBean1:" + this.applicationBean1);
                    Log.d("Dagger", "OtherClass applicationBean2:" + this.applicationBean2);
                }
            }
        }


1.9、结果分析

        我们运行之后打印出来的Log如下图:
        
        我们来分析Log的表现:
        1、Application中注入的mAppBean1和mAppBean2以及Activity中注入的applicationBean1、applicationBean2还有OtherClass中注入的applicationBean1、applicationBean2这六个对象的地址都是95c5354
        分析:
            1、在Activity和OtherClass中我们可以获取到ApplicationBean对象,说明我们当前的注入方式完成了"Activity从Application继承Component进行注入"的任务
            2、我们不仅在APP的全局都获取到了ApplicationBean对象,而且得到的都是单例对象,这说明我们在ApplicationModule中对ApplicationBean进行单例注入的方式在全局都是有效的
        2、Activity中的activityBean和OtherClass中的activityBean对象地址不同
        分析:
            ActivityBean对象在Activity中和OtherClass中分别注入了两次,所以这两次注入是独立的,它们注入的ActivityBean对象是不同的
        至此,该注入方式我们就介绍完毕,下面我们来介绍另一种继承的方式。


二、使用Subcomponent的方式进行继承注入

        如果对比源码看的话, 请将源码分支切换到UseSubcomponent分支


2.1、如何注入

        该方式和上面的方式区别之处只有三个地方:


1、改造Activity的Component对象

        我们需要先来改造Activity的Component对象,也就是ActivityComponent,需要将其改写为如下的方式:
        @ForActivity
        @Subcomponent(modules = ActivityModule.class)
        public interface ActivityComponent {
            void inject(MainActivity activity);


            void inject(MainActivity.OtherClass otherClass);
        }
        它与之前的方式的区别有两点:
        1、不再使用@Component而使用@Subcomponent来注释
        2、删除了"dependencies = ApplicationComponent.class"语句


2、改造Application的Component对象

        然后我们来改造Application的Component对象也就是ApplicationComponent,将其改造成如下方式:
        @Singleton
        @Component(modules = ApplicationModule.class)
        public interface ApplicationComponent {
            //注入DaggerApplication
            void inject(DaggerApplication application);


            //说明将BeanForApplication开放给其他Component使用
            ApplicationBean providerAppBean();


            ActivityComponent activityComponent();
        }
        这里的改造只是多了一句声明:ActivityComponent activityComponent()


3、改造Activity中的注入方式

        我们还需要改造Activity和OtherClass中的注入方式,改造成如下方式(Activity和OtherClass的注入方式相同):
            DaggerApplication application = (DaggerApplication) getApplication();
            ApplicationComponent applicationComponent = application.getAppComponent();
            applicationComponent.activityComponent().inject(this);
        然后就完成了所有改造,运行结果如下:
        
        这个结果与dependencies的方式结果是一致的,说明两种注入方式都达到了Component继承的目的。


三、dependencies与Subcomponent注入方式的区别

        这两种方式的区别其实在Activity注入时就可以看出来,我们再次贴出它们的对比:
        dependencies方式:
            DaggerApplication application = (DaggerApplication) getApplication();
            //获取ApplicationComponent对象
            ApplicationComponent applicationComponent = application.getAppComponent();
            //用ActivityComponent对象进行注入
            ActivityComponent activityComponent = DaggerActivityComponent.builder().applicationComponent(applicationComponent).build();
            activityComponent.inject(this);
        Subcomponent方式:
            DaggerApplication application = (DaggerApplication) getApplication();
            ApplicationComponent applicationComponent = application.getAppComponent();
            //用ApplicationComponent对象进行注入
            applicationComponent.activityComponent().inject(this);
        结果发现,dependencies方式中,我们最终调用的是ActivityComponent对象中的inject()方法,而Subcomponent方式中,我们最终调用的是ApplicationComponent的inject()方法。
        从Component的注释上我们也可以看到这个区别:
        dependencies方式:
            //ApplicationComponent
            @Component(modules = ApplicationModule.class)
            public interface ApplicationComponent {
                ......
            }


            //ActivityComponent 
            @Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class)
            public interface ActivityComponent {
                ......
            }
        Subcomponent方式:
            //ApplicationComponent
            @Component(modules = ApplicationModule.class)
            public interface ApplicationComponent {
                ......
                ActivityComponent activityComponent();
            }
            
            //ActivityComponent
            @Subcomponent(modules = ActivityModule.class)
            public interface ActivityComponent {
                ......
            }
        对比中我们发现, dependencies中Component强调的是在子类Component依赖于某个Component(子类为主角),而Subcomponent中强调的则是在父类Component中提供某个子类的Component(父类为主角)


四、如何选择两种继承方式

        那么该如何选择这两种继承方式呢?
        在Stackoverflow中就有人提出了这样的问题(http://stackoverflow.com/questions/29587130/dagger-2-subcomponents-vs-component-dependencies),简单理解就是:
        dependencies方式让Component之间更加独立,结构更加清晰,也更利于解耦。
        所以该如何选择是否已经有了答案呢?

        至此,Dagger2系列介绍就全部结束,下课。

        我们再次列出本系列所有的总结:

        《Dagger2教程一之配置(原)》

        《Dagger2教程二之基础使用(原)》

        《Dagger2教程三之构造方法带参数的情况(原)》

        《Dagger2教程四之多构造方法的情况(原)》

        《Dagger2教程五之单例模式(原)》

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/u010961631/article/details/72626134

智能推荐

WCE Windows hash抓取工具 教程_wce.exe -s aaa:win-9r7tfgsiqkf:0000000000000000000-程序员宅基地

文章浏览阅读6.9k次。WCE 下载地址:链接:https://share.weiyun.com/5MqXW47 密码:bdpqku工具界面_wce.exe -s aaa:win-9r7tfgsiqkf:00000000000000000000000000000000:a658974b892e

各种“网络地球仪”-程序员宅基地

文章浏览阅读4.5k次。Weather Globe(Mackiev)Google Earth(Google)Virtual Earth(Microsoft)World Wind(NASA)Skyline Globe(Skylinesoft)ArcGISExplorer(ESRI)国内LTEarth(灵图)、GeoGlobe(吉奥)、EV-Globe(国遥新天地) 软件名称: 3D Weather Globe(http:/_网络地球仪

程序员的办公桌上,都出现过哪些神奇的玩意儿 ~_程序员展示刀,产品经理展示枪-程序员宅基地

文章浏览阅读1.9w次,点赞113次,收藏57次。我要买这些东西,然后震惊整个办公室_程序员展示刀,产品经理展示枪

霍尔信号、编码器信号与电机转向-程序员宅基地

文章浏览阅读1.6w次,点赞7次,收藏63次。霍尔信号、编码器信号与电机转向从电机出轴方向看去,电机轴逆时针转动,霍尔信号的序列为编码器信号的序列为将霍尔信号按照H3 H2 H1的顺序组成三位二进制数,则霍尔信号翻译成状态为以120°放置霍尔为例如不给电机加电,使用示波器测量三个霍尔信号和电机三相反电动势,按照上面所说的方向用手转动电机得到下图① H1的上升沿对应电机q轴与H1位置电角度夹角为0°,..._霍尔信号

个人微信淘宝客返利机器人搭建教程_怎么自己制作返利机器人-程序员宅基地

文章浏览阅读7.1k次,点赞5次,收藏36次。个人微信淘宝客返利机器人搭建一篇教程全搞定天猫淘宝有优惠券和返利,仅天猫淘宝每年返利几十亿,你知道么?技巧分享:在天猫淘宝京东拼多多上挑选好产品后,按住标题文字后“复制链接”,把复制的淘口令或链接发给机器人,复制机器人返回优惠券口令或链接,再打开天猫或淘宝就能领取优惠券啦下面教你如何搭建一个类似阿可查券返利机器人搭建查券返利机器人前提条件1、注册微信公众号(订阅号、服务号皆可)2、开通阿里妈妈、京东联盟、拼多多联盟一、注册微信公众号https://mp.weixin.qq.com/cgi-b_怎么自己制作返利机器人

【团队技术知识分享 一】技术分享规范指南-程序员宅基地

文章浏览阅读2.1k次,点赞2次,收藏5次。技术分享时应秉持的基本原则:应有团队和个人、奉献者(统筹人)的概念,同时匹配团队激励、个人激励和最佳奉献者激励;团队应该打开工作内容边界,成员应该来自各内容方向;评分标准不应该过于模糊,否则没有意义,应由客观的基础分值以及分团队的主观综合结论得出。应有心愿单激励机制,促进大家共同聚焦到感兴趣的事情上;选题应有规范和框架,具体到某个小类,这样收获才有目标性,发布分享主题时大家才能快速判断是否是自己感兴趣的;流程和分享的模版应该有固定范式,避免随意的格式导致随意的内容,评分也应该部分参考于此;参会原则,应有_技术分享

随便推点

O2OA开源企业办公开发平台:使用Vue-CLI开发O2应用_vue2 oa-程序员宅基地

文章浏览阅读1k次。在模板中,我们使用了标签,将由o2-view组件负责渲染,给o2-view传入了两个参数:app="内容管理数据"和name="所有信息",我们将在o2-view组件中使用这两个参数,用于展现“内容管理数据”这个数据应用下的“所有信息”视图。在o2-view组件中,我们主要做的事是,在vue组件挂载后,将o2的视图组件,再挂载到o2-view组件的根Dom对象。当然,这里我们要在我们的O2服务器上创建好数据应用和视图,对应本例中,就是“内容管理数据”应用下的“所有信息”视图。..._vue2 oa

[Lua]table使用随笔-程序员宅基地

文章浏览阅读222次。table是lua中非常重要的一种类型,有必要对其多了解一些。

JAVA反射机制原理及应用和类加载详解-程序员宅基地

文章浏览阅读549次,点赞30次,收藏9次。我们前面学习都有一个概念,被private封装的资源只能类内部访问,外部是不行的,但这个规定被反射赤裸裸的打破了。反射就像一面镜子,它可以清楚看到类的完整结构信息,可以在运行时动态获取类的信息,创建对象以及调用对象的属性和方法。

Linux-LVM与磁盘配额-程序员宅基地

文章浏览阅读1.1k次,点赞35次,收藏12次。Logical Volume Manager,逻辑卷管理能够在保持现有数据不变的情况下动态调整磁盘容量,从而提高磁盘管理的灵活性/boot分区用于存放引导文件,不能基于LVM创建PV(物理卷):基于硬盘或分区设备创建而来,生成N多个PE,PE默认大小4M物理卷是LVM机制的基本存储设备,通常对应为一个普通分区或整个硬盘。创建物理卷时,会在分区或硬盘的头部创建一个保留区块,用于记录 LVM 的属性,并把存储空间分割成默认大小为 4MB 的基本单元(PE),从而构成物理卷。

车充产品UL2089安规测试项目介绍-程序员宅基地

文章浏览阅读379次,点赞7次,收藏10次。4、Dielecteic voltage-withstand test 介电耐压试验。1、Maximum output voltage test 输出电压试验。6、Resistance to crushing test 抗压碎试验。8、Push-back relief test 阻力缓解试验。7、Strain relief test 应变消除试验。2、Power input test 功率输入试验。3、Temperature test 高低温试验。5、Abnormal test 故障试验。

IMX6ULL系统移植篇-系统烧写原理说明_正点原子 imx6ull nand 烧录-程序员宅基地

文章浏览阅读535次。镜像烧写说明_正点原子 imx6ull nand 烧录