手记

【Kotlin中使用Dagger2】基础入门篇(二)

概述

在前面文章【Kotlin中使用Dagger2】基础入门篇一中我们介绍了使用Dagger2,通过构造方法实现了最基本的依赖注入。

这种注入方式我们需要在依赖类的构造方法上添加@Inejct。但如果目标类的类型是一个接口或者是一个第三方库的类呢由于接口不能直接实例化@Inject标注构造方法就行不通同样第三方库我们没办法直接修改其构造方法@Inject也使用不了那我们这一小节就来介绍一下如何使用Dagger2注入接口类型和第三方库中的类型。

本节内容

  • @Module
  • @Provides
  • Component与Module的关系
  • 递归注入
  • 维度优先级

代码引入

首先我们定了一个接口

    interface MainService {
        fun getMainInfo():String
    }

接下来接口实现类返回一个字符串

    class MainServiceImpl :MainService {
        override fun getMainInfo(): String {
            return "This is main info"
        }
    }

最后在MainActivity中接口类型变量声明@Inject标注

    @Inject
    lateinit var mMainService:MainService

@Module

如上代码我们需要注入的是接口类型Dagger2并不知道它的实现类是什么为了让Dagger2能够注入该接口我们需要引入一个新的注解@Module直接看代码

    @Module
    class MainModule {

    }

以上代码我们定个了一个类MainModule使用@Module进行了标注这表示MainModule是一个创建实例的工厂Dagger2需要实例化对象可以到这个类来找实例化方法。

@Provides

在上面的代码里边我们创建一个实例化工厂但是并没有实例化如何创建Dagger2能够识别的实例化方法这就需要另一个注解@Provides代码如下

    @Module
    class MainModule {

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

可以看到在MainModule中我们创建一个方法返回一个接口实现类的实例并使用@Provides标注了该方法这就是具体的创建实例的方法。

所以@Module和@Provides是配套出现的当然在这个工厂MainModule里边你还可以添加更多的实例化方法根据具体的业务对应添加。

Component与Module的关系

现在有创建实例的工厂了如何让Dagger2知道它的存在并使用它来实例化对象这又需要Component与Module产生关联代码如下

    @Component(modules = [(MainModule::class)])
    interface MainComponent {
        fun inject(activity:MainActivity)
    }

这个Component和之前相比唯一的区别就是在@Component中多了一个属性modules它是一个数组可以添加多个的module。从这也可以看出来Component又多了一个职责管理Module同时让Dagger2知道这个Component中需要实例化的话可以到它管理的Module中去寻找对应的实例化方法。

最后编译一下编译一下编译一下看下调用层的代码

    class MainActivity : AppCompatActivity() {

        @Inject
        lateinit var mMainService:MainService

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

            initInjection()

            mClickBtn.setOnClickListener {
                toast(mMainService.getMainInfo())
            }
        }
    
        /*
            Dagger2注入注册
         */
        private fun initInjection() {
            DaggerMainComponent.builder().mainModule(MainModule()).build().inject(this)
        }
    }

可以看到Dagger2注册时发生一点变化多出来一个mainModule方法这个方法其实就是Component管理的Module名称同时传入Module工厂作为参数。如果管理着多个Module依次构建对应方法。

递归注入

上面的代码已经基本完成了接口的注入但是有一点我们并不是很满意既然整个过程都是使用Dagger2来注入那么工厂方法返回时是不是也可以通过注入来实例化MainServiceImpl而不是直接实例化。直接上代码

首先实现类需要修改

    class MainServiceImpl @Inject constructor():MainService {
        override fun getMainInfo(): String {
            return "This is main info"
        }
    }

接下来工厂方法需要修改

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

可以看到我们把实现类修改为了@Inject标注的构造方法。同时工厂方法中传入了实现类作为参数。

Dagger2在调用工厂方法时发现它存在参数首先会实例化参数我们的参数MainServiceImpl是通过@Inject标注了构造方法可直接实例化成功。

当参数存在多个并且有嵌套时会依次递归实例化参数列表最后完成工厂方法调用。

维度优先级

大家明白了递归注入后可以看到在Dagger2的注入过程中同时有两种方法一种就是@Inject构造方法一个就是@Module工厂。那如果一个类既有@Inject构造方法也有@Module工厂创建谁的优先级更高呢

请注意@Module工厂的方式优先级高于@Inject构造方法。在整个递归过程中都是这样如果在工厂中找到了实例化方法直接返回不会再去走@Inject构造方法。

相信大家掌握了Module工厂的方式第三方库的注入也不会有什么问题了修改工厂方法直接返回第三方库的实例就行了。

小结

本小节通过@Module和@Provides配合生成创建实例的工厂通过Component管理工厂可实现接口和第三方库的注入。合理的运用两种维度去实现依赖注入会让你的代码清晰很多代码层次也会一目了然。

16人推荐
随时随地看视频
慕课网APP

热门评论

MainServiceImpl 也通过依赖注入, 是要消灭new, 将依赖注入进行到底啊!!!


查看全部评论