查看: 2808|回复: 0

[Android教程] 【Kotlin中使用Dagger2】基础入门篇(二)

发表于 2018-1-20 08:00:04
概述

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

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

本节内容 @Module @Provides Component与Module的关系 递归注入 维度优先级 代码引入

首先我们定了一个接口

  1. interface MainService {
  2. fun getMainInfo():String
  3. }
复制代码

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

  1. class MainServiceImpl :MainService {
  2. override fun getMainInfo(): String {
  3. return "This is main info"
  4. }
  5. }
复制代码

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

  1. @Inject
  2. lateinit var mMainService:MainService
复制代码
@Module

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

  1. @Module
  2. class MainModule {
  3. }
复制代码

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

@Provides

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

  1. @Module
  2. class MainModule {
  3. @Provides
  4. fun provideMainService():MainService{
  5. return MainServiceImpl()
  6. }
  7. }
复制代码

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

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

Component与Module的关系

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

  1. @Component(modules = [(MainModule::class)])
  2. interface MainComponent {
  3. fun inject(activity:MainActivity)
  4. }
复制代码

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

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

  1. class MainActivity : AppCompatActivity() {
  2. @Inject
  3. lateinit var mMainService:MainService
  4. override fun onCreate(savedInstanceState: Bundle?) {
  5. super.onCreate(savedInstanceState)
  6. setContentView(R.layout.activity_main)
  7. initInjection()
  8. mClickBtn.setOnClickListener {
  9. toast(mMainService.getMainInfo())
  10. }
  11. }
  12. /*
  13. Dagger2注入注册
  14. */
  15. private fun initInjection() {
  16. DaggerMainComponent.builder().mainModule(MainModule()).build().inject(this)
  17. }
  18. }
复制代码

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

递归注入

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

首先,实现类需要修改:

  1. class MainServiceImpl @Inject constructor():MainService {
  2. override fun getMainInfo(): String {
  3. return "This is main info"
  4. }
  5. }
复制代码

接下来,工厂方法需要修改:

  1. @Provides
  2. fun provideMainService(service: MainServiceImpl):MainService{
  3. return service
  4. }
复制代码

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

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

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

维度优先级

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

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

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

小结

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

更多精彩应用《Kotlin打造完整电商APP》



回复

使用道具 举报