跳至主要內容

Dagger 2 ❤️ Android

JI,XIAOYONG...大约 5 分钟dagger2

前言

上篇文章open in new window介绍了Dagger 2 的基本使用,本文跟随官方文档open in new window实践一下Dagger 2 Android中的使用,可以看做是官方文档的不完全翻译。

本文有关Dagger 2的使用分为ActivityFragment两部分,二者的使用几乎没有差别,最后介绍一下在 Google 官方 Demo 中学到的一个小技巧,可以将几乎所有的和Dagger 2的逻辑放到一份代码里面,对Android工程的影响极小。

首先要添加相关依赖(Kotlin 环境):

apply plugin: 'kotlin-kapt'//引用该插件
implementation "com.google.dagger:dagger:$rootProject.dagger2Version"
implementation "com.google.dagger:dagger-android-support:$rootProject.dagger2Version"//Android 特需
kapt "com.google.dagger:dagger-compiler:$rootProject.dagger2Version"//注意如果是 kotlin 语言,这里需要时#kapt#

在 Activity 中的使用

Application 范围内的@Component

首先创建整个应用程序使用的@Component,并将AndroidInjectionModule加入其中,实现Inject注入入口:

@Component(modules = [AndroidInjectionModule::class, MainActivityModule::class])
interface AppComponent {

    fun inject(application: MainApplication)
}

AppComponent的范围是整个应用程序都有效。

创建单个 Activity 的@Subcomponent

创建某个Activity专属的@Subcomponent,用于提供AndroidInjector.Builder

@Subcomponent
interface MainActivitySubComponent : AndroidInjector<MainActivity> {

    @Subcomponent.Builder
    abstract class Builder : AndroidInjector.Builder<MainActivity>()

}

创建单个 Activity 的@Module

创建属于整个Activity@Module,注意这里要指明@subcomponents为刚刚创建的MainActivitySubComponent

@Module(includes = [MainActivityModule.InnerModule::class], subcomponents = [MainActivitySubComponent::class])
class MainActivityModule {

    @Provides
    fun bindWaitForInjectClass() = WaitForInjectClass()

    @Module
    abstract class InnerModule {
        @Binds
        @IntoMap
        @ClassKey(MainActivity::class)
        abstract fun bindInjectorFactory(builder: MainActivitySubComponent.Builder): AndroidInjector.Factory<*>//注意这里是 Factory<*>
    }
}

class WaitForInjectClass //一个供依赖注入的类

可以看到在MainActivityModule中提供了一个方法利用刚刚MainActivitySubComponent中提供的MainActivitySubComponent.Builder实例生成了一个AndroidInjector.Factory,而这个Factory就是我们后面要将MainActivityModule中的依赖实例通过AppComponent传递给MainActivity实例的关键。

此外还可以看到提供该 Factory 的方法是放到了另外一个抽象类里面然后再导入 MainActivityModule 中的,这是因为该方法的注解@Binds 要求方法是抽象的,而 MainActivityModule 要是需要给 Activity 提供依赖实例所必须的@Provides 又要求类不能是抽象的,否则就要求该方法是静态的。权衡之下我觉得这种方式是比较能接受的,当然也不排除有其他更优雅的解决方案,欢迎提Issueopen in new window告知。

然后,将MainActivityModule加入到应用程序的@Component——AppComponent中。

使 Application 继承自 HasActivityInjectoropen in new window

使当前MainApplication继承自HasActivityInjector,该接口只有一个方法:

/** Returns an {@link AndroidInjector} of {@link Activity}s. */
AndroidInjector<Activity> activityInjector();

这个类是用来为相应的Activity提供一个AndroidInjector。由于我们已经在AppComponent中包括了AndroidInjectionModule,所以Dagger 2已经可以自动为我们注入DispatchingAndroidInjector依赖,所以接下来的代码如下:

class MainApplication : HasActivityInjector, Application() {

    override fun onCreate() {
        super.onCreate()
        DaggerAppComponent.create().inject(this)
    }

    @Inject
    lateinit var dispatchActivityInjector: DispatchingAndroidInjector<Activity>

    override fun activityInjector() = dispatchActivityInjector//返回 Dagger 2 为我们注入的 dispatchActivityInjector 对象
}

onCreate()方法中传入当前Application的依赖。

在 Activity 中使用自动注入依赖

做完了以上所有内容,我们只需要在Activity中添加如下代码就可以实现自动注入:

class MainActivity : AppCompatActivity() {

    @Inject
    lateinit var waitForInjectClass: WaitForInjectClass

    override fun onCreate(savedInstanceState: Bundle?) {
        AndroidInjection.inject(this)//注意这里,在 super() 之前调用
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_dagger2_x_android_main)

        Log.d("TAG", "The Class is $waitForInjectClass")
    }
}

以上所有代码如下,或者也可以在这里找到open in new window

这一切是怎么实现的呢?

Android程序运行时,AndroidInjection.inject()Application中的activityInjector()方法获取到 DispatchingAndroidInjector<Activity> ,然后将Activity传入inject(Activity)

DispatchingAndroidInjector 通过AppComponent找到我们在MainActivityModule提供的对应的AndroidInjector.Factory,然后创建了 AndroidInjector ——这就是我们当前Activity对应的MainActivitySubComponent

接下来便按照之前的逻辑,从MainActivitySubComponent中查找提供waitForInjectClass的实例方法完成注入。

在 Fragment 中的使用

Dagger 2Fragment的使用和在Activity中的使用十分相似。

通过之前的代码我们可以知道,其基本的原理依旧是利用@Component@subcomponent@Module之间的关联关系将ApplicationActivity等的依赖注入通过AndroidInjector关联起来的:

MainActivitySubComponent通过将MainActivityModule加入到AppComponent之中,然后当MainActivity之中需要使用到MainActivitySubComponent时,又通过AndroidInjectorAppComponent中拿到MainActivityModule中的AndroidInjector.Factory,通过该FactoryMainActivitySubComponent中的Builder产生关联,从而获取到了MainActivitySubComponent的实例供Activity使用。

Fragment中我们也可以这样处理,只不过由于Fragment的特性,他的@Module不仅可以交给Application@Component,也可以交给其他Fragment或者Activity@Component,让其实现HasFragmentInjector即可,这取决于我们想要给Fragment绑定的依赖。

具体的实现一般分为下面几步:

  • 创建Application@Component并添加AndroidInjectionModule

  • 创建实现了AndroidInjector<MainFragment>MainFragmentSubComponent,其内部有方法提供AndroidInjector.Builder<MainFragment>

  • 创建包含了提供AndroidInjector.Factory<*>的抽象方法的MainFragmentModule,指定其subcomponentsMainFragmentSubComponent

  • MainFragmentSubComponent加入到想要加入的类的@Component中,比如AppComponent

  • Application(如果上一步是Activity,则本步也是Activity等)中参照在Activity实现的步骤实现HasFragmentInjector

    上述完整的代码如下,或者也可以在这里找到open in new window

关于Fragment加入到Activity的 Demo 在官方文档有,这里就不再赘述了,其实只要掌握原理,其他用法的完全可以触类旁通。

一个小技巧

通过观察上面的两份代码,我们发现虽然这Dagger 2已经替我们做了好多事情,我们只需要在需要使用依赖注入的类中使用诸如AndroidInjection.inject(this)这样的代码就可以了,但是如果ActivityFragment类过多的时候,这样的重复性工作仍然是个不小的工作量,万一有某处遗忘了便会导致出错。

这时就可以用到我在Google 官方示例代码open in new window中学到的一个小技巧了 (针对本文中的例子做了一些修改),或者你也可以到这里查看源码open in new window

参考资料

Dagger 2 官方文档 Android 篇open in new window

Google 官方示例代码——GithubBrowserSampleopen in new window

文章标题:《Dagger 2 ❤️ Android》
本文作者: JI,XIAOYONG
发布时间: 2019/01/27 21:23:50 UTC+8
更新时间: 2023/12/30 16:17:02 UTC+8
written by human, not by AI
本文地址: https://jixiaoyong.github.io/blog/posts/c04fdc6c.html
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 许可协议。转载请注明出处!
你认为这篇文章怎么样?
  • 0
  • 0
  • 0
  • 0
  • 0
  • 0
评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v2.15.8