查看: 2931|回复: 0

[Android教程] 【Kotlin中使用Dagger2】进阶提升篇(一)

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

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

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

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

本节内容 Component组织结构 Component服务能力 Component依赖(Dependencies) Component组织结构

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

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

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

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

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

ApplicationComponent(Application级别,管理App全局实例) ActivityComponent(Activity级别,管理Activity通用实例,Fragment同理) 业务Component(业务层面,根据具体业务划分)

代码如下:

首先,定义出App的Application:

  1. class MainApplication:Application(){
  2. }
复制代码

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

  1. /*
  2. Application级别Component
  3. */
  4. @Component(modules = [(ApplicationModule::class)])
  5. interface ApplicationComponent {
  6. }
  7. /*
  8. Application级别Module
  9. */
  10. @Module
  11. class ApplicationModule(private val context:MainApplication) {
  12. @Provides
  13. fun provideContext():Context{
  14. return this.context
  15. }
  16. }
复制代码

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

  1. /*
  2. Activity级别Component
  3. */
  4. @Component(modules = [(ActivityModule::class)])
  5. interface ActivityComponent {
  6. }
  7. /*
  8. Activity级别Module
  9. */
  10. @Module
  11. class ActivityModule(private val activity: Activity) {
  12. @Provides
  13. fun provideActivity(): Activity {
  14. return this.activity
  15. }
  16. }
复制代码

最后,业务级别Component和Module:(Demo就直接使用前两节的代码)

  1. /*
  2. 业务级Component
  3. */
  4. @Component(modules = [(MainModule::class)])
  5. interface MainComponent {
  6. fun inject(activity:MainActivity)
  7. }
  8. /*
  9. 业务级Module
  10. */
  11. @Module
  12. class MainModule {
  13. @Provides
  14. fun provideMainService(service: MainServiceImpl):MainService{
  15. return service
  16. }
  17. }
复制代码

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

Component服务能力

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

先看一下代码:

  1. /*
  2. Application级别Component
  3. */
  4. @Component(modules = [(ApplicationModule::class)])
  5. interface ApplicationComponent {
  6. fun context():Context
  7. }
复制代码
  1. /*
  2. Activity级别Component
  3. */
  4. @Component(modules = [(ActivityModule::class)])
  5. interface ActivityComponent {
  6. fun activity():Activity
  7. }
复制代码

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

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

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

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

两种定义方法的区别:

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

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

Component依赖(Dependencies)

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

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

  1. /*
  2. Activity级别Component
  3. */
  4. @Component(dependencies = [(ApplicationComponent::class)],modules = [(ActivityModule::class)])
  5. interface ActivityComponent {
  6. fun activity():Activity
  7. fun context(): Context
  8. }
  9. /*
  10. 业务级Component
  11. */
  12. @Component(dependencies = [ActivityComponent::class],modules = [(MainModule::class)])
  13. interface MainComponent {
  14. fun inject(activity:MainActivity)
  15. }
复制代码

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

  1. class MainActivity : AppCompatActivity() {
  2. @Inject
  3. lateinit var mContext:Context
  4. @Inject
  5. lateinit var mActivity:Activity
  6. @Inject
  7. lateinit var mMainService:MainService
  8. override fun onCreate(savedInstanceState: Bundle?) {
  9. super.onCreate(savedInstanceState)
  10. setContentView(R.layout.activity_main)
  11. initInjection()
  12. mClickBtn.setOnClickListener {
  13. if (mContext == null){
  14. toast("context is null")
  15. }
  16. if (mActivity == null){
  17. toast("activity is null")
  18. }
  19. toast(mMainService.getMainInfo())
  20. }
  21. }
  22. /*
  23. Dagger2注入注册
  24. */
  25. private fun initInjection() {
  26. val applicationComponent = DaggerApplicationComponent.builder().applicationModule(ApplicationModule(application as MainApplication)).build()
  27. val activityComponent = DaggerActivityComponent.builder().applicationComponent(applicationComponent).activityModule(ActivityModule(this)).build()
  28. DaggerMainComponent.builder().activityComponent(activityComponent).mainModule(MainModule()).build().inject(this)
  29. }
  30. }
复制代码

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

最后,我们再封装一下:

ApplicationComponent的初始化放到MainApplication(manifest记得设置MainApplication) 新增一个Activity基类(BaseActivity),ActivityComponent初始化放到BaseActivity MainActivity继承BaseActivity,直接使用封装好的Component

MainApplication代码:

  1. class MainApplication:Application(){
  2. lateinit var mApplicationComponent:ApplicationComponent
  3. override fun onCreate() {
  4. super.onCreate()
  5. initAppInjection()
  6. }
  7. /*
  8. Application Component初始化
  9. */
  10. private fun initAppInjection() {
  11. mApplicationComponent = DaggerApplicationComponent.builder().applicationModule(ApplicationModule(this)).build()
  12. }
  13. }
复制代码

BaseActivity代码:

  1. open class BaseActivity:AppCompatActivity() {
  2. lateinit var mActivityComponent: ActivityComponent
  3. override fun onCreate(savedInstanceState: Bundle?) {
  4. super.onCreate(savedInstanceState)
  5. initActivityInjection()
  6. }
  7. private fun initActivityInjection() {
  8. mActivityComponent = DaggerActivityComponent.builder().applicationComponent((application as MainApplication).mApplicationComponent).activityModule(ActivityModule(this)).build()
  9. }
  10. }
复制代码

MainActivity代码:

  1. class MainActivity : BaseActivity() {
  2. @Inject
  3. lateinit var mContext:Context
  4. @Inject
  5. lateinit var mActivity:Activity
  6. @Inject
  7. lateinit var mMainService:MainService
  8. override fun onCreate(savedInstanceState: Bundle?) {
  9. super.onCreate(savedInstanceState)
  10. setContentView(R.layout.activity_main)
  11. initInjection()
  12. mClickBtn.setOnClickListener {
  13. if (mContext == null){
  14. toast("context is null")
  15. }
  16. if (mActivity == null){
  17. toast("activity is null")
  18. }
  19. toast(mMainService.getMainInfo())
  20. }
  21. }
  22. /*
  23. Dagger2注入注册
  24. */
  25. private fun initInjection() {
  26. DaggerMainComponent.builder().activityComponent(mActivityComponent).mainModule(MainModule()).build().inject(this)
  27. }
  28. }
复制代码
小结

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

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



回复

使用道具 举报