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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,458评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,454评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,171评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,062评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,440评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,661评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,906评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,609评论 0 200
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,379评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,600评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,085评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,409评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,072评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,088评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,860评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,704评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,608评论 2 270

推荐阅读更多精彩内容