Dagger2高级使用

这篇文章介绍如下内容
相关代码在这里DaggerDemo

  • 两种建立Component之间联系的方式。
    • @Component的Dependencies
    • @Subcomponent
  • @Scope和@Singleton
  • @Qualifier(限定符)和@Named

两种建立Component之间联系的方式。

@Component的Dependencies

获取某些类需要用到其他类,比如说SharedPreferences,就需要上下文,所以这时候可以使用Dependencies

首先写一个提供Context的依赖Component

  • 创建ActivityComponent,ActivityModule
@Component(modules = [(ActivityModule::class)])
interface ActivityComponent {
    fun getContext():Context
}

@Module
class ActivityModule(private val context: Context) {
    @Provides
    fun provideContext():Context{
        return context
    }
}

会发现ActivityComponent 与之前MainComponent不同,并不是fun inject(activity: MainActivity),而是fun getContext():Context

  • 这两种方式有如下的区别
    • fun inject(需注入的类: 类名),如fun inject(activity: MainActivity):这种方式下Dagger2会从目标类开始查找@Inject注解,自动生成依赖注入的代码,调用inject可完成依赖的注入。
    • fun get类名():类名,如fun getContext():Context:这种方式下Dagger2会到生成目标类实例,供其它组件使用(如果Obj本身还包含其它依赖注入,也会自动生成对应实例)。

新建DependenciesComponent,DependenciesModule

  • 在DependenciesComponent 通过dependencies来依赖ActivityComponent
@Component(dependencies = [(ActivityComponent::class)],modules = [(DependenciesModule::class)])
interface DependenciesComponent {
    fun inject(activity:DependenciesActivity)
}
  • 在DependenciesModule 中provideSharePreferences的方法参数使用Context
@Module
class DependenciesModule {
    @Provides
    fun provideSharePreferences(context: Context): SharedPreferences {
        return context.getSharedPreferences("app", Context.MODE_PRIVATE)
    }
}

在Activity完成初始化Dagger

初始化Dagger与之前不同,首先要初始化DaggerActivityComponent,再将其赋值给DaggerDependenciesComponent

class DependenciesActivity : AppCompatActivity() {
    @Inject
    lateinit var preferences: SharedPreferences
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_advanced)
        initDagger()
        preferences.edit().putString("test", "依赖").commit()
        btn_dependencies.setOnClickListener {
            Toast.makeText(this, preferences.getString("test", ""), Toast.LENGTH_SHORT).show()
        }
    }
    private fun initDagger() {
        val activityComponent = DaggerActivityComponent.builder()
                .activityModule(ActivityModule(this))
                .build()
        DaggerDependenciesComponent.builder()
                .activityComponent(activityComponent)
                .dependenciesModule(DependenciesModule())
                .build()
                .inject(this)
    }
}

@Subcomponent

新建Child,ChildModule,ChildComponent

class Child @Inject constructor() {
    fun getChildName():String{
        return "孩子"
    }
}

@Module
class ChildModule{
    @Provides
    fun providesChild(): Child {
        return Child()
    }
}

@Subcomponent(modules = [(ChildModule::class)])
interface ChildComponent {
    fun inject(activity: SubActivity)
}

ChildModule与之前相同,但是ChildComponent 的注解改为@Subcomponent

新建Parent,ParentModule,ParentComponent

class Parent @Inject constructor() { 
    fun getParentName():String{
        return "父亲"
    }
}
@Module
class ParentModule {
    @Provides
    fun providesParent(): Parent {
        return Parent()
    }
}
@Component(modules = [(ParentModule::class)])
interface ParentComponent {
    fun addSub(module: ChildModule): ChildComponent
}

在ParentComponent 添加方法addSub将其与ChildComponent关联

在Activity完成初始化Dagger

class SubActivity : AppCompatActivity() {
    
    @Inject
    lateinit var parent: Parent
    
    @Inject
    lateinit var child: Child
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_sub)
        initDagger()
        btn_sub.setOnClickListener {
            Toast.makeText(this, parent.getParentName(), Toast.LENGTH_SHORT).show()
            Toast.makeText(this, child.getChildName(), Toast.LENGTH_SHORT).show()
        }
    }
    
    private fun initDagger() {
        DaggerParentComponent.builder()
                .parentModule(ParentModule())
                .build()
                .addSub(ChildModule())
                .inject(this)
    }
}

初始化与之前不同,先完成ParentComponent 的建造之后通过之前预留的addSub方法将ChildComponent与其连接

两种方式的总结

  1. @Component的dependencies 的能单独使用,而@Subcomponent必须由父级Component调用方法获取。
  2. @Component的dependencies 可以显示它依赖于那个Component, 而@Subcomponent的子级Component并不知道其的父级Component

@Scope和@Singleton

@Scope是用来管理依赖的生命周期的。而@Singleton则是@Scope的默认实现,不过它指的是不是单例,它的作用只是保证依赖在@Component中是唯一的

使用@Singleton

  • 在ParentModule 和ParentComponent 增加@Singleton注解
@Singleton
@Component(modules = [(ParentModule::class)])
interface ParentComponent {
    fun addSub(module: ChildModule): ChildComponent
}
@Module
class ParentModule {
    @Singleton
    @Provides
    fun providesParent(): Parent {
        return Parent()
    }
}
  • 在Activity中增加这些东西
    @Inject
    lateinit var parent1: Parent
    @Inject
    lateinit var parent2: Parent

    btn_singleton.setOnClickListener {
            Log.d("==", parent1.toString())
            Log.d("==", parent2.toString())
    }

之后你就可以看到在日志中看到这两个hash值是相同的,也就是同一个

parent1 = Parent@957fb47
parent2 = Parent@957fb47

然后你用同样的方式在使用Dependencies的ActivityComponent,ActivityModule中,然后它就报错了。
因为Dagger规定依赖的子Component(比如DependenciesComponent)也必须有注解
然后你给DependenciesComponent,DependenciesModule增加了@Singleton还是报错
因为Dagger规定不允许依赖的子Component使用同样的@Singleton,所以我们要自定义@Scope注解

自定义@Scope注解

自定义DependenciesScope使用在DependenciesComponent,DependenciesModule就可以了

@Scope
@Documented
@Retention(AnnotationRetention.RUNTIME) annotation class DependenciesScope

@Qualifier(限定符)和@Named

@Qualifier是限定符,而@Named则是基于String的限定符
当我有两个相同的依赖(都继承某一个父类或者都是先某一个接口)可以提供给高层时,那么程序就不知道我们到底要提供哪一个依赖,因为它找到了两个。
这时候我们就可以通过限定符为两个依赖分别打上标记,指定提供某个依赖。

@Named使用

  • 有两个实现IJob的类
class AndroidProgrammer : IJob {
    override fun work(context: Context) {
        Toast.makeText(context, "工作内容:开发AndroidApp", Toast.LENGTH_SHORT).show()
    }
}

class iOSProgrammer: IJob {
    override fun work(context: Context) {
        Toast.makeText(context, "工作内容:开发iOSApp", Toast.LENGTH_SHORT).show()
    }
}
  • 在MainModule中增加Provides的方法,并使用@Named注解
    @Named("Android")
    @Provides
    fun provideAndroid():IJob{
        return AndroidProgrammer()
    }
    @Named("iOS")
    @Provides
    fun provideiOS():IJob{
        return iOSProgrammer()
    }
  • 在Activity中
    切记,将@Named(“Android”)改为@field:[Named(“Android”)] 使用
    @field:[Named("Android")]
    @Inject
    lateinit var android:IJob
   
    @field:[Named("iOS")]
    @Inject
    lateinit var iOS:IJob
        
    btn_name.setOnClickListener {
            android.work(this)
            iOS.work(this)
    }

除此之外我们还可以自定义@Qualifier注解

自定义@Qualifier注解

  • 新增@AndroidQualifier,@iOSQualifier
@Qualifier
@Documented
@Retention(AnnotationRetention.RUNTIME)
annotation class AndroidQualifier

@Qualifier
@Documented
@Retention(AnnotationRetention.RUNTIME)
annotation class iOSQualifier
  • 在MainModule中
    @AndroidQualifier
    @Provides
    fun provideAndroid2():IJob{
        return AndroidProgrammer()
    }

    @iOSQualifier
    @Provides
    fun provideiOS2():IJob{
        return iOSProgrammer()
    }
  • 在Activity中
@field:[AndroidQualifier]
@Inject
lateinit var android2:IJob

@field:[iOSQualifier]
@Inject
lateinit var iOS2:IJob
    原文作者:辩护人
    原文地址: https://www.jianshu.com/p/5bdaf393a13c
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞