为了账号安全,请及时绑定邮箱和手机立即绑定

【Kotlin中使用Dagger2】进阶提升篇(一)

标签:
Android

在前面文章【Kotlin中使用Dagger2】基础入门篇一【Kotlin中使用Dagger2】基础入门篇二中我们介绍了使用Dagger2从两种维度去实现依赖注入这也是Dagger2最核心的功能。

在一个完整App开发过程中如果只使用之前介绍的内容代码层次结构就会显得过于复杂使用方式也是简单粗暴完全不能体现出Dagger2这样一个神器的威力。

从这一小节开始我们将陆续介绍如何更加优雅的使用Dagger2Component层次结构划分作用域的使用限定符的使用等等。

  • Component组织结构
  • Component服务能力
  • Component依赖Dependencies

大家试想一下一个完整的App中如果只使用一个Component会出现什么情况Component中的代码量是不是特别大可能会出现无数个inject方法并且对应的Module也会变得臃肿因为它要提供无数个工厂方法这些方法还是不同业务对应的这样的话代码就会显得特别混乱。

所以我们需要拆分Component如何拆分

我们可以根据单一职责的原则把独立业务拆分出来每个业务对应一个Component比如登录、注册、找回密码可以划分到用户管理业务UserComponent商品分类、商品列表、商品详情可以划分到商品业务GoodsComponent等等这些都是属于业务层面的Component。

除了业务层面我们还得考虑通用性层面比如全局ContextActivity实例等等。全局Context我们可以放到Application级别Activity实例我们可以放到Activity级别。如果说需要对Fragment封装通用性同Activity级别根据实际情况划分。

那从上至下Component就会被划分为

  • ApplicationComponentApplication级别管理App全局实例
  • ActivityComponentActivity级别管理Activity通用实例Fragment同理
  • 业务Component业务层面根据具体业务划分

代码如下

首先定义出App的Application

    class MainApplication:Application(){

    }

接下来Application级别Component和ModuleModule中提供了Context工厂方法Cotnext本身不能直接实例化通过Module实例化时作为参数传入

    /*
        Application级别Component
     */
    @Component(modules = [(ApplicationModule::class)])
    interface ApplicationComponent {

    }

    /*
        Application级别Module
     */
    @Module
    class ApplicationModule(private val context:MainApplication) {

        @Provides
        fun provideContext():Context{
            return this.context
        }
    }

接下来Activity级别Component和ModuleModule中提供了Activity工厂方法Activity本身不能直接实例化通过Module实例化时作为参数传入

    /*
        Activity级别Component
     */
    @Component(modules = [(ActivityModule::class)])
    interface ActivityComponent {

    }

    /*
        Activity级别Module
     */
    @Module
    class ActivityModule(private val activity: Activity) {

        @Provides
        fun provideActivity(): Activity {
            return this.activity
        }
    }

最后业务级别Component和ModuleDemo就直接使用前两节的代码

    /*
        业务级Component
     */
    @Component(modules = [(MainModule::class)])
    interface MainComponent {
        fun inject(activity:MainActivity)
    }

    /*
        业务级Module
     */
    @Module
    class MainModule {

        @Provides
        fun provideMainService(service: MainServiceImpl):MainService{
            return service
        }
    }

一个基本的Component结构就出来了业务层相关代码和前两节代码一样MainActivityMainService等。

大家可以看到ApplicationComponent和ActivityComponent中并没有提供任何方法因为它们是通用性的组件它们需要提供的是通用服务供业务层使用而不会像业务Component一样直接注入到某个业务层面上。那么如何让它们提供通用性的能力在通用的Component中这里给大家介绍一种新的方法定义。

先看一下代码

    /*
        Application级别Component
     */
    @Component(modules = [(ApplicationModule::class)])
    interface ApplicationComponent {

        fun context():Context
    }
    /*
        Activity级别Component
     */
    @Component(modules = [(ActivityModule::class)])
    interface ActivityComponent {

        fun activity():Activity
    }

可以看到这两个Component中定义的方法与业务层定义的方法是不一样的。

ApplicationComponent中定义了一个方法它的返回值是Context。

ActivityComponent中定义了一个方法它的返回值是Activity。

而业务层的Component定义的inject方法是没有返回值直接连接到目标类。

两种定义方法的区别

  • fun inject(obj:目标类)从目标类开始查找@Inject注解生成依赖注入的代码
  • fun xxx():Obj生成Obj实例供其它组件使用如果Obj本身还包含其它依赖注入也会自动生成对应实例

所以ApplicationComponent和ActivityComponent提供的是一种服务能力供业务组件使用服务能力的来源就是对应Module的工厂方法。

说了这么多我们的业务Component和通用Component现在还没有任何的直接关系 为了让业务Component拥有Context和Activity的能力有多种方法,我们这里介绍一种方式。

使用Component的dependencies属性从上至下的进行依赖ActivityComponent依赖于ApplicationComponent业务Component依赖于ActivityComponent代码如下

    /*
        Activity级别Component
     */
    @Component(dependencies = [(ApplicationComponent::class)],modules = [(ActivityModule::class)])
    interface ActivityComponent {

        fun activity():Activity
        fun context(): Context
    }

    /*
        业务级Component
     */
    @Component(dependencies = [ActivityComponent::class],modules = [(MainModule::class)])
    interface MainComponent {
        fun inject(activity:MainActivity)
    }

可以看到在依赖关系建立之后ActivityComponent中多出来一个方法context()这是因为Dagger2不能跨级共享服务能力也就是MainComponent依赖于ActivityComponent就只能使用ActivityComponent的activity实例不能使用ApplicationComponent中的context除非在ActivityComponent中暴露context的实例否则在业务层使用了Context的注入编译就会报错。
最后看下调层的完整代码

    class MainActivity : AppCompatActivity() {

        @Inject
        lateinit var mContext:Context

        @Inject
        lateinit var mActivity:Activity

        @Inject
        lateinit var mMainService:MainService

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)

            initInjection()

            mClickBtn.setOnClickListener {
                if (mContext == null){
                    toast("context is null")
                }

                if (mActivity == null){
                    toast("activity is null")
                }
                toast(mMainService.getMainInfo())
            }
        }

        /*
            Dagger2注入注册
         */
        private fun initInjection() {

            val applicationComponent = DaggerApplicationComponent.builder().applicationModule(ApplicationModule(application as MainApplication)).build()
            val activityComponent = DaggerActivityComponent.builder().applicationComponent(applicationComponent).activityModule(ActivityModule(this)).build()
            DaggerMainComponent.builder().activityComponent(activityComponent).mainModule(MainModule()).build().inject(this)

        }
    }

这样的依赖关系需要一层一层构建Component实例因为需要作为参数传入到下一层Component。

最后我们再封装一下

  • ApplicationComponent的初始化放到MainApplicationmanifest记得设置MainApplication
  • 新增一个Activity基类BaseActivityActivityComponent初始化放到BaseActivity
  • MainActivity继承BaseActivity直接使用封装好的Component

MainApplication代码

    class MainApplication:Application(){

        lateinit var mApplicationComponent:ApplicationComponent

        override fun onCreate() {
            super.onCreate()

            initAppInjection()

        }

        /*
         Application Component初始化
        */
        private fun initAppInjection() {
            mApplicationComponent = DaggerApplicationComponent.builder().applicationModule(ApplicationModule(this)).build()
        }
    }

BaseActivity代码

    open class BaseActivity:AppCompatActivity() {

        lateinit var mActivityComponent: ActivityComponent

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)

            initActivityInjection()

        }

        private fun initActivityInjection() {
            mActivityComponent = DaggerActivityComponent.builder().applicationComponent((application as MainApplication).mApplicationComponent).activityModule(ActivityModule(this)).build()

        }
    }

MainActivity代码

    class MainActivity : BaseActivity() {

        @Inject
        lateinit var mContext:Context

        @Inject
        lateinit var mActivity:Activity

        @Inject
        lateinit var mMainService:MainService

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)

            initInjection()

            mClickBtn.setOnClickListener {
                if (mContext == null){
                    toast("context is null")
                }

                if (mActivity == null){
                    toast("activity is null")
                }
                toast(mMainService.getMainInfo())
            }
        }

        /*
            Dagger2注入注册
         */
        private fun initInjection() {
            DaggerMainComponent.builder().activityComponent(mActivityComponent).mainModule(MainModule()).build().inject(this)

        }
    }

这一小节介绍了Component的组织结构以及新的方法定义配置依赖关系等。在ApplicationComponent和ActivityComponent中我们只提供了context和activity的服务能力大家在实际开发中可以添加更多的服务Application对应全局唯一性配置Activity对应Activity层面的能用配置等。配置依赖关系的方法不止dependencies这一种后面我们会介绍@Subcomponent来配置Component之间的关系。同时我们只介绍了Activity级别像Fragment也是可以单独作为通用组件使用根据实际情况修改即可。

点击查看更多内容
14人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
全栈工程师
手记
粉丝
1.7万
获赞与收藏
251

关注作者,订阅最新文章

阅读免费教程

感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消