Dagger2教程六之Component的组织方法(原)

Posted 工程师阿杜

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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教程五之单例模式(原)》

以上是关于Dagger2教程六之Component的组织方法(原)的主要内容,如果未能解决你的问题,请参考以下文章

dagger2简单使用与理解笔记

dagger2的使用总结

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

Dagger2 scope

dagger2系列之生成类实例(自己)

dagger2系列之依赖方式