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

智能推荐

51单片机的中断系统_51单片机中断篇-程序员宅基地

文章浏览阅读3.3k次,点赞7次,收藏39次。CPU 执行现行程序的过程中,出现某些急需处理的异常情况或特殊请求,CPU暂时中止现行程序,而转去对异常情况或特殊请求进行处理,处理完毕后再返回现行程序断点处,继续执行原程序。void 函数名(void) interrupt n using m {中断函数内容 //尽量精简 }编译器会把该函数转化为中断函数,表示中断源编号为n,中断源对应一个中断入口地址,而中断入口地址的内容为跳转指令,转入本函数。using m用于指定本函数内部使用的工作寄存器组,m取值为0~3。该修饰符可省略,由编译器自动分配。_51单片机中断篇

oracle项目经验求职,网络工程师简历中的项目经验怎么写-程序员宅基地

文章浏览阅读396次。项目经验(案例一)项目时间:2009-10 - 2009-12项目名称:中驰别克信息化管理整改完善项目描述:项目介绍一,建立中驰别克硬件档案(PC,服务器,网络设备,办公设备等)二,建立中驰别克软件档案(每台PC安装的软件,财务,HR,OA,专用系统等)三,能过建立的档案对中驰别克信息化办公环境优化(合理使用ADSL宽带资源,对域进行调整,对文件服务器进行优化,对共享打印机进行调整)四,优化完成后..._网络工程师项目经历

LVS四层负载均衡集群-程序员宅基地

文章浏览阅读1k次,点赞31次,收藏30次。LVS:Linux Virtual Server,负载调度器,内核集成, 阿里的四层SLB(Server Load Balance)是基于LVS+keepalived实现。NATTUNDR优点端口转换WAN性能最好缺点性能瓶颈服务器支持隧道模式不支持跨网段真实服务器要求anyTunneling支持网络private(私网)LAN/WAN(私网/公网)LAN(私网)真实服务器数量High (100)High (100)真实服务器网关lvs内网地址。

「技术综述」一文道尽传统图像降噪方法_噪声很大的图片可以降噪吗-程序员宅基地

文章浏览阅读899次。https://www.toutiao.com/a6713171323893318151/作者 | 黄小邪/言有三编辑 | 黄小邪/言有三图像预处理算法的好坏直接关系到后续图像处理的效果,如图像分割、目标识别、边缘提取等,为了获取高质量的数字图像,很多时候都需要对图像进行降噪处理,尽可能的保持原始信息完整性(即主要特征)的同时,又能够去除信号中无用的信息。并且,降噪还引出了一..._噪声很大的图片可以降噪吗

Effective Java 【对于所有对象都通用的方法】第13条 谨慎地覆盖clone_为继承设计类有两种选择,但无论选择其中的-程序员宅基地

文章浏览阅读152次。目录谨慎地覆盖cloneCloneable接口并没有包含任何方法,那么它到底有什么作用呢?Object类中的clone()方法如何重写好一个clone()方法1.对于数组类型我可以采用clone()方法的递归2.如果对象是非数组,建议提供拷贝构造器(copy constructor)或者拷贝工厂(copy factory)3.如果为线程安全的类重写clone()方法4.如果为需要被继承的类重写clone()方法总结谨慎地覆盖cloneCloneable接口地目的是作为对象的一个mixin接口(详见第20_为继承设计类有两种选择,但无论选择其中的

毕业设计 基于协同过滤的电影推荐系统-程序员宅基地

文章浏览阅读958次,点赞21次,收藏24次。今天学长向大家分享一个毕业设计项目基于协同过滤的电影推荐系统项目运行效果:项目获取:https://gitee.com/assistant-a/project-sharing21世纪是信息化时代,随着信息技术和网络技术的发展,信息化已经渗透到人们日常生活的各个方面,人们可以随时随地浏览到海量信息,但是这些大量信息千差万别,需要费事费力的筛选、甄别自己喜欢或者感兴趣的数据。对网络电影服务来说,需要用到优秀的协同过滤推荐功能去辅助整个系统。系统基于Python技术,使用UML建模,采用Django框架组合进行设

随便推点

你想要的10G SFP+光模块大全都在这里-程序员宅基地

文章浏览阅读614次。10G SFP+光模块被广泛应用于10G以太网中,在下一代移动网络、固定接入网、城域网、以及数据中心等领域非常常见。下面易天光通信(ETU-LINK)就为大家一一盘点下10G SFP+光模块都有哪些吧。一、10G SFP+双纤光模块10G SFP+双纤光模块是一种常规的光模块,有两个LC光纤接口,传输距离最远可达100公里,常用的10G SFP+双纤光模块有10G SFP+ SR、10G SFP+ LR,其中10G SFP+ SR的传输距离为300米,10G SFP+ LR的传输距离为10公里。_10g sfp+

计算机毕业设计Node.js+Vue基于Web美食网站设计(程序+源码+LW+部署)_基于vue美食网站源码-程序员宅基地

文章浏览阅读239次。该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流项目运行环境配置:项目技术:Express框架 + Node.js+ Vue 等等组成,B/S模式 +Vscode管理+前后端分离等等。环境需要1.运行环境:最好是Nodejs最新版,我们在这个版本上开发的。其他版本理论上也可以。2.开发环境:Vscode或HbuilderX都可以。推荐HbuilderX;3.mysql环境:建议是用5.7版本均可4.硬件环境:windows 7/8/10 1G内存以上;_基于vue美食网站源码

oldwain随便写@hexun-程序员宅基地

文章浏览阅读62次。oldwain随便写@hexun链接:http://oldwain.blog.hexun.com/ ...

渗透测试-SQL注入-SQLMap工具_sqlmap拖库-程序员宅基地

文章浏览阅读843次,点赞16次,收藏22次。用这个工具扫描其它网站时,要注意法律问题,同时也比较慢,所以我们以之前写的登录页面为例子扫描。_sqlmap拖库

origin三图合一_神教程:Origin也能玩转图片拼接组合排版-程序员宅基地

文章浏览阅读1.5w次,点赞5次,收藏38次。Origin也能玩转图片的拼接组合排版谭编(华南师范大学学报编辑部,广州 510631)通常,我们利用Origin软件能非常快捷地绘制出一张单独的绘图。但是,我们在论文的撰写过程中,经常需要将多种科学实验图片(电镜图、示意图、曲线图等)组合在一张图片中。大多数人都是采用PPT、Adobe Illustrator、CorelDraw等软件对多种不同类型的图进行拼接的。那么,利用Origin软件能否实..._origin怎么把三个图做到一张图上

51单片机智能电风扇控制系统proteus仿真设计( 仿真+程序+原理图+报告+讲解视频)_电风扇模拟控制系统设计-程序员宅基地

文章浏览阅读4.2k次,点赞4次,收藏51次。51单片机智能电风扇控制系统仿真设计( proteus仿真+程序+原理图+报告+讲解视频)仿真图proteus7.8及以上 程序编译器:keil 4/keil 5 编程语言:C语言 设计编号:S0042。_电风扇模拟控制系统设计