Dagger 2 完全解析(四),在Android中的使用

Dagger 2 完全解析(四),在Android中的使用


[Dagger 2 完全解析(一),基本使用与原理
Dagger 2 完全解析(二), 进阶使用
Dagger 2 完全解析(三), Component 与 SubComponent
Dagger 2 完全解析(四),在Android中的使用

本系列文章是基于 Google Dagger 2.23.2 版本, Kotlin 1.3.21版本


本文:https://blog.csdn.net/xiaowu_zhu/article/details/95933256

在Android项目中使用Dagger2时,像 ActivityFragment这种类型的初始化操作都是有Android系统提供的,如果要往其注入对象,不免会有这样的写法:

class MainActivity : AppCompatActivity() {

    @Inject
    lateinit var activity: MainActivity

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        DaggerMainActivityComponent.builder()
            .mainActivityModule(MainActivityModule(this))
            .appComponent(
                (application as MyApplication)
                    .daggerAppComponent
            ).build()
            .inject(this)
    }
}

上述写法,会有以下几个我们几乎无法避免的问题:

  1. 上述Dagger部分的代码存在模板代码,在每个Activity中都会这么写到,即便抽取到BaseActivity中,也有一些特殊部分需要在每个Activity中单独处理,随着业务的增加后期维护不易;

  2. 从上面的代码可以看出,MainActivityModule中持有了MainActivity实例,并且需要在MainActivity传入参数,它打破了依赖注入的核心原则:类不应该知道它是如何注入的。

因此为了解决上面的问题,谷歌官方推出了dagger.android

下面是基本的使用方式,包括如何注入ActivityFragment,本篇只讲如何使用,后续再分析其原理。

引入Dagger.android依赖

build.gradle中添加:

implementation 'com.google.dagger:dagger-android:2.23.2'
implementation 'com.google.dagger:dagger-android-support:2.23.2'
kapt 'com.google.dagger:dagger-android-processor:2.23.2'

注入Activity

MainActivity为例

Dagger2中的写法

class MainActivity : AppCompatActivity() {
    // 这里只是做了一个例子
    @Inject
    lateinit var activity: MainActivity

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        DaggerMainActivityComponent.builder()
            .mainActivityModule(MainActivityModule(this))
            .appComponent(
                (application as MyApplication)
                    .daggerAppComponent
            ).build()
            .inject(this)
    }
}

@ActivityScope
@Component(modules = [MainActivityModule::class], dependencies = [AppComponent::class])
interface MainActivityComponent {
    fun inject(activity: MainActivity)
}

@Module
class MainActivityModule(private val activity: MainActivity) {
    @Provides
    fun provideMainActivity(): MainActivity = activity
}

@Scope
annotation class ActivityScope

Dagger2-Android中的写法

修改MainActivityComponent

@ActivityScope
@Subcomponent
interface MainActivityComponent : AndroidInjector<MainActivity> {
	// 声明MainActivity创建的工厂接口
    @dagger.Subcomponent.Factory
    interface Factory : AndroidInjector.Factory<MainActivity>
}

修改MainActivityModule

@Module(subcomponents = [MainActivityComponent::class])
abstract class MainActivityModule {
	// module中提供绑定工厂方法
    @Binds
    @IntoMap
    @ClassKey(MainActivity::class)
    abstract fun bind(
        factory: MainActivityComponent.Factory
    ): AndroidInjector.Factory<*>
}

修改onCreate()中的Dagger注入

override fun onCreate(savedInstanceState: Bundle?) {
   // 在super.onCreate前添加
   AndroidInjection.inject(this)
   super.onCreate(savedInstanceState)
   setContentView(R.layout.activity_main)
}

修改MyApplication

通过dagger.android注入时,Application中也发生了相应的变化

class MyApplication : Application(), HasAndroidInjector {

    @Inject
    lateinit var dispatchingAndroidInjector: DqispatchingAndroidInjector<Any>

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

    override fun androidInjector(): AndroidInjector<Any> {
        return dispatchingAndroidInjector
    }
}

@dagger.Component(
    modules = [AndroidInjectionModule::class,// 引入框架提供的InjectionModule
        AndroidSupportInjectionModule::class,// 如果使用了support相关的类,也需要引入
        MainActivityModule::class // 这是我们自定义的Module
    ]
)
interface AppComponent : AndroidInjector<MyApplication> {
    @dagger.Component.Factory
    interface Factory : AndroidInjector.Factory<MyApplication>
}

我们需要将我们在MainActivity中的MainActivityModule加入到Application中的AppComponentmodule,然后Make ProjectMake Appbuild project,如果未报错即成功。

简洁写法

如果相应的ActivityComponent中只有以下操作时:

@ActivityScope
@Subcomponent
interface MainActivityComponent : AndroidInjector<MainActivity> {
	// 声明MainActivity创建的工厂接口
    @dagger.Subcomponent.Factory
    interface Factory : AndroidInjector.Factory<MainActivity>
}

我们可以新建一个module类, 将满足以上条件的Component 集中到一起,并删除对应的Component,如:

@Module
abstract class ActivityBindingModule {
    @ActivityScoped
    @ContributesAndroidInjector(modules = [MainActivityModule.class])
    abstract fun mainActivity() : MainActivity

    @ActivityScoped
    @ContributesAndroidInjector(modules = [AddEditTaskModule.class])
    abstract fun addEditTaskActivity(): AddEditTaskActivity 
}

删除 原有module中的bind*方法

@Module
public class MainActivityModule{
    
}

注入Fragment

注入Fragment与注入Activity类似,唯一不同的地方在于我们需要在onAttach方法中执行AndroidSupportInject.inject(this)

class BlankFragment : Fragment() {

    override fun onAttach(context: Context?) {
        AndroidSupportInjection.inject(this)
        super.onAttach(context)
    }
}

@dagger.Module
abstract class FragmentBindModule {
    @ContributesAndroidInjector
    abstract fun blankFragment(): BlankFragment
}


@dagger.Component(
    modules = [AndroidInjectionModule::class,
        AndroidSupportInjectionModule::class,
        ActivityBinder::class,
        FragmentBindModule::class
    ]
)
interface AppComponent : AndroidInjector<MyApplication> {

    @dagger.Component.Factory
    interface Factory : AndroidInjector.Factory<MyApplication>
}

对于FragmentBindModule不仅可以放入到AppComponent中,也可以放入到MainActivityComponent,或者FragmentComponent如:

@dagger.Subcomponent(modules = [FragmentBindModule::class])
interface MainActivityComponent : AndroidInjector<MainActivity>{

    @dagger.Subcomponent.Factory
    interface Factory: AndroidInjector.Factory<MainActivity>
}


@dagger.Module(subcomponents = [
    MainActivityComponent::class // 将fragment放入到了activity对应的Component
])
abstract class MainActivityModule{

    @Binds
    @IntoMap
    @ClassKey(MainActivity::class)
    abstract fun bind(factory:MainActivityComponent.Factory):AndroidInjector.Factory<*>
}

一些问题

@Model 注解的是抽象类时 @provides 标注的必须是静态方法

A @Module may not contain both non-static @Provides methods and abstract @Binds or @Multibinds declarations

这个错误提示是将Module定义成了抽象类,这在java中只需要将@Provide标注的方法设置为静态方法即可,但是在Kotlin中是行不通的,因为在kotlin中静态方法是写在companion object代码块内,所以解决方法有两种:

  • Module标注companion object

    @dagger.Module
    abstract class MainActivityModule() {
        @dagger.Module
        companion object {
            @JvmStatic
            @Provides
            fun provideFragment() = BlankFragment()
        }
    
        @Binds
        abstract fun context(activity: MainActivity): Context
    }
    
  • 使用多个Module将抽象方法和@provide标注的方法分开,如:

    @Module
    abstract class MainActivityBindsModule {
        @Binds
        abstract fun context(activity: MainActivity): Context
    }
    
    @Module(includes = arrayOf(MainActivityBindsModule ::class))
    class MainActivityProvidesModule {
          @Provides
          fun provideFragment() = BlankFragment()
    }
    

总结

通过前面的例子我们亦可以看出:

  1. Dagger2-android 使用的是Dagger2中的继承关系
  2. 使用它,我们就可以不用写那么多的模版代码,相对于Dagger2方便了许多;
  3. Dagger2-Android 比Dagger2更强大,我们使用@Binds绑定抽象方法来注入一些提供初始化的类等。
posted @ 2019-07-14 22:17  jxiaow  阅读(48)  评论(0编辑  收藏