kotlin使用Dagger2的過(guò)程全紀(jì)錄
前言
Dagger2作為依賴(lài)注入神器,相信很多朋友都聽(tīng)說(shuō)過(guò)它的大名。只不過(guò)它的有些概念,理解起來(lái)并不是那么清晰,并且在使用的過(guò)程中,也比較迷糊。
Dagger2有Google接手開(kāi)發(fā)的一個(gè)基于JSR-330標(biāo)準(zhǔn)的依賴(lài)注入框架,它會(huì)在編譯期間自動(dòng)生成相關(guān)代碼,負(fù)責(zé)依賴(lài)對(duì)象的創(chuàng)建,達(dá)到解耦目的。
下面將詳細(xì)介紹關(guān)于kotlin使用Dagger2的相關(guān)內(nèi)容,分享出來(lái)供大家參考學(xué)習(xí),下面話不多說(shuō)了,來(lái)一起看看詳細(xì)的介紹吧。
kotlin中配置Dagger2
在app模塊的build.gradle文件中進(jìn)行如下配置,關(guān)于kapt的相關(guān)知識(shí)。
apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-kapt' android { ... } dependencies { ... implementation 'com.google.dagger:dagger:2.11' kapt 'com.google.dagger:dagger-compiler:2.11' }
相關(guān)常用注解:
- @Inject
- @Component
- @Module
- @Provides
- @Qualifier和@Named
- @Scope和@Singleton
@Inject
@Inject注解只是JSR-330中定義的注解,在javax.inject包中。 這個(gè)注解本身并沒(méi)有作用,它需要依賴(lài)于注入框架才具有意義,可以用來(lái)標(biāo)記構(gòu)造函數(shù)、屬性和方法。
標(biāo)記構(gòu)造函數(shù)
被標(biāo)記的構(gòu)造函數(shù)可以有0個(gè)或多個(gè)依賴(lài)作為參數(shù)。
同一個(gè)類(lèi)中最多只可以標(biāo)記一個(gè)構(gòu)造函數(shù)。
class People @Inject constructor(val name:String = "Tom")
注意在kotlin中這種寫(xiě)法是不被允許的,因?yàn)檫@等價(jià)于java中的多個(gè)構(gòu)造方法People(String name), People()
正確的寫(xiě)法應(yīng)該是這樣:
data class People constructor(val name: String) { @Inject constructor() : this("Tom") }
標(biāo)記屬性
被標(biāo)記的屬性不能是final的,kotlin中不能是val。
被注入進(jìn)的屬性不能用private修飾(是Dagger2不支持,而非@Inject不支持)。
@Inject lateinit var people:People
標(biāo)記方法
被標(biāo)記的方法可以有0個(gè)或多個(gè)依賴(lài)作為參數(shù)。
方法不能是抽象的。
class HomeActivity : AppCompatActivity() { private lateinit var people:People @Inject fun setPeople(people:People){ this.people = people } }
這種方法注入和屬性注入并沒(méi)有什么本質(zhì)上的不同,實(shí)現(xiàn)效果也基本一樣。還有一種做法是@Inject標(biāo)記被注入類(lèi)的某個(gè)方法,該方法會(huì)在類(lèi)的構(gòu)造方法之后接著被調(diào)用:
data class People constructor(val name: String) { @Inject constructor() : this("Tom") init { println("init:$name") } @Inject fun hello(){ println("hello:$name") } } class HomeActivity : AppCompatActivity() { @Inject lateinit var people:People override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_home) //執(zhí)行相關(guān)注入操作 ... println(people.toString()) } }
運(yùn)行結(jié)果是這樣的:
01-02 11:57:30.995 16601-16601/? I/System.out: init:Tom 01-02 11:57:30.995 16601-16601/? I/System.out: hello:Tom 01-02 11:57:30.995 16601-16601/? I/System.out: People(name=Tom)
@Component
可以理解為一個(gè)注射器,可以算是Dagger2中最核心的一個(gè)注解,用來(lái)標(biāo)記一個(gè)接口或者抽象類(lèi)。使用@Component標(biāo)記的接口,會(huì)在編譯時(shí)自動(dòng)生成一個(gè)Dagger+類(lèi)名的實(shí)現(xiàn)類(lèi)實(shí)現(xiàn)依賴(lài)注入。在Component中一般可以定義兩種方法:
Members-injection methods:
該方法有一個(gè)參數(shù),表示需要注入到的類(lèi),提醒Dagger在該類(lèi)中尋找需要被注入的屬性(被@Inject標(biāo)記)。
void inject(SomeType someType);//無(wú)返回值 SomeType injectAndReturn(SomeType someType);//返回它的參數(shù)類(lèi)型
等價(jià)于:
MembersInjector<SomeType> getMembersInjector();//使用MembersInjector.injectMembers方法注入
Provision methods:
該方法沒(méi)有參數(shù),返回一個(gè)需要被注入(或被提供)的依賴(lài)。一般用于為其他Component提供依賴(lài)的時(shí)候。
SomeType getSomeType(); Provider<SomeType> getSomeTypeProvider();//可以通過(guò)Provider.get訪問(wèn)任意次 Lazy<SomeType> getLazySomeType();//通過(guò)Lazy.get第一次訪問(wèn)時(shí)創(chuàng)建實(shí)例,并在之后的訪問(wèn)中都訪問(wèn)同一個(gè)實(shí)例
@Component interface HomeComponent { fun inject(activity: HomeActivity) fun injectAndReturn(activity: HomeActivity): HomeActivity fun getInjectors(): MembersInjector<HomeActivity> fun getPeople():People }
事實(shí)上,了解到這里我們已經(jīng)可以使用最簡(jiǎn)單的Dagger2用法,畢竟有了依賴(lài)和注射器,只需要注入就可以了,我們來(lái)看一個(gè)最簡(jiǎn)單的Dagger2實(shí)例,只使用@Inject和@Component來(lái)完成注入。
第一步:在需要被注入的類(lèi)的構(gòu)造方法上添加注解@Inject
class People @Inject constructor() { fun hello(){ println("hello") } }
第二步:編寫(xiě)一個(gè)注射器接口
@Component interface HomeComponent { fun inject(activity: HomeActivity) }
第三步:注入
class HomeActivity : AppCompatActivity() { @Inject lateinit var people:People override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_home) DaggerHomeComponent.builder() .build() .inject(this)//會(huì)在這句代碼時(shí)執(zhí)行注入的操作 people.hello() } } 03-01 14:30:23.425 3256-3256/? I/System.out: hello //大功告成
當(dāng)然,上面這種只是最簡(jiǎn)單的用法,如果需要傳入一些非自定義類(lèi)的實(shí)例就不適用了,畢竟你不能在第三方的類(lèi)中加入@Inject注解。此時(shí)就需要用到@Module和@Provides注解。
@Module
用來(lái)標(biāo)記類(lèi),為Component提供依賴(lài),相當(dāng)于告訴Component,如果需要依賴(lài)可以來(lái)找我,當(dāng)然前提是在Component中配置了該Module。同時(shí)Module可以通過(guò)includes依賴(lài)其他的Module。
@Provides
用來(lái)標(biāo)記Module中的方法,該方法的返回類(lèi)型是你需要提供的依賴(lài)類(lèi)型。
舉個(gè)自己項(xiàng)目中的例子,我需要在presenter中創(chuàng)建一個(gè)pl2303對(duì)象,pl2303對(duì)象的創(chuàng)建又需要context和pl2303Interface,所以我們需要提供三個(gè)依賴(lài),因?yàn)閏ontext在其他地方也要用,我們單獨(dú)提出來(lái):
@Module class ContextModule(private var mContext: Context) { @Provides fun getContext() = mContext }
pl2303Interface只有這一個(gè)地方要用:
@Module(includes = arrayOf(ContextModule::class)) class Pl2303Module(private var pl2303Interface: ActivityCallBridge.PL2303Interface) { @Provides fun providePl2303(mContext: Context): Pl2303 { return Pl2303(mContext, pl2303Interface) } }
其中includes可以是多個(gè),我們這里把ContextModule加進(jìn)來(lái),這樣創(chuàng)建pl2303就只差一個(gè)pl2303Interface,這是個(gè)接口對(duì)象,不能new,從構(gòu)造函數(shù)注入進(jìn)來(lái)。接下來(lái)創(chuàng)建注射器:
@Component(modules = arrayOf(Pl2303Module::class)) interface MainPresenterComponent { fun inject(presenter: MainPresenter) }
最后注入:
class MainPresenter(val view: MainContract.View) : MainContract.Presenter, ActivityCallBridge.PL2303Interface, LifecycleObserver { @Inject lateinit var pl2303: Pl2303 init { DaggerMainPresenterComponent.builder() .contextModule(ContextModule(view.context)) .pl2303Module(Pl2303Module(this)) .build() .inject(this) } }
如果在大型項(xiàng)目中,一個(gè)Component有很多的Module,那么不需要傳入?yún)?shù)的Module是可以省略的,看一下官方的注釋文檔:
public static void main(String[] args) { OtherComponent otherComponent = ...; MyComponent component = DaggerMyComponent.builder() // required because component dependencies must be set(必須的) .otherComponent(otherComponent) // required because FlagsModule has constructor parameters(必須的) .flagsModule(new FlagsModule(args)) // may be elided because a no-args constructor is visible(可以省略的) .myApplicationModule(new MyApplicationModule()) .build(); }
@Named和@Qualifier
@Named是@Qualifier的一個(gè)實(shí)現(xiàn)。有時(shí)候我們會(huì)需要提供幾個(gè)相同類(lèi)型的依賴(lài)(比如繼承于同一父類(lèi)),如果不做處理的話編譯器就不知道我們需要的具體是哪一個(gè)依賴(lài)而報(bào)錯(cuò),比如這樣:
abstract class Animal class Dog : Animal() { override fun toString(): String { return "dog" } } class Cat : Animal() { override fun toString(): String { return "cat" } } @Module class AnimalModule { @Provides fun provideDog(): Animal = Dog() @Provides fun provideCat(): Animal = Cat() } data class Pet @Inject constructor(val pet: Animal)
這時(shí)候就需要標(biāo)記一下來(lái)告訴編譯器我們需要的是哪個(gè)依賴(lài):
@Module class AnimalModule { @Provides @Named("dog") fun provideDog(): Animal = Dog() @Provides @Named("cat") fun provideCat(): Animal = Cat() } data class Pet @Inject constructor(@Named("dog") val pet: Animal)
上面我們說(shuō)了@Named只是@Qualifier的一個(gè)實(shí)現(xiàn)而已,所以我們也可以用@Qualifier來(lái)達(dá)到一樣的效果,實(shí)際使用中也更推薦使用@Qualifier的方式,因?yàn)锧Named需要手寫(xiě)字符串來(lái)進(jìn)行標(biāo)識(shí),容易出錯(cuò)。
使用@Qualifier需要注意:
- 創(chuàng)建一個(gè)自定義的Qualifier至少需要@Qualifier, @Retention(RUNTIME)這兩個(gè)注解。
- 可以有自己的屬性。
我們可以看一下@Named的源碼來(lái)加深一下理解:
@Qualifier @Documented @Retention(RUNTIME) public @interface Named { /** The name. */ String value() default ""; }
下面我們比葫蘆畫(huà)瓢來(lái)改造一下上面的例子:
@Module class AnimalModule { @Provides @DogAnim fun provideDog(): Animal = Dog() @Provides @CatAnim fun provideCat(): Animal = Cat() } @Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class DogAnim @Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class CatAnim data class Pet @Inject constructor(@CatAnim val pet: Animal)
經(jīng)測(cè)試依然是可以運(yùn)行的。
Pet(pet=cat)
@Scope和@Singleton
A scope annotation applies to a class containing an injectable constructor and governs how the injector reuses instances of the type
@Scope是用來(lái)標(biāo)記包含可注入構(gòu)造函數(shù)的類(lèi)或者提供注入依賴(lài)對(duì)象的類(lèi),簡(jiǎn)單來(lái)說(shuō),可以用來(lái)標(biāo)記包含@Inject構(gòu)造函數(shù)的類(lèi)或者@Module類(lèi)。
@Scope是用來(lái)管理依賴(lài)的生命周期的。它和@Qualifier一樣是用來(lái)自定義注解的,而@Singleton和@Named類(lèi)似,是@Scope的默認(rèn)實(shí)現(xiàn)。
如果一個(gè)注射器和創(chuàng)建依賴(lài)對(duì)象的地方?jīng)]有標(biāo)記@Scope,那么每次注入時(shí)都會(huì)創(chuàng)建一個(gè)新的對(duì)象,如果標(biāo)記了@Scope,則在規(guī)定的生命周期內(nèi)會(huì)使用同一個(gè)對(duì)象,特別注意是在規(guī)定的生命周期內(nèi)單例,并不是全局單例,或者可以理解為在@Component內(nèi)單例。
還是借助上面的例子:
data class People constructor(val name: String) { @Inject constructor() : this("Tom") init { println("init:$name") } @Inject fun hello(){ println("hello:$name") } } class HomeActivity : AppCompatActivity() { @Inject lateinit var people: People @Inject lateinit var people_2: People override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_home) DaggerHomeComponent.builder() .build() .inject(this) println("people===people_2:${people===people_2}") } }
運(yùn)行結(jié)果:
people===people_2:false
說(shuō)明確實(shí)是兩個(gè)不同的對(duì)象,接下來(lái)我們改造一下:
@Singleton data class People constructor(val name: String) { ...//和之前一樣 } @Singleton @Component(modules = arrayOf(AnimalModule::class)) interface HomeComponent { fun inject(activity: HomeActivity) } ...//HomeActivity代碼和之前一樣
再次看下運(yùn)行結(jié)果:
people===people_2:true
說(shuō)明這次兩次都是訪問(wèn)的同一個(gè)對(duì)象。上面提到這只是一個(gè)局部單例,那么怎么實(shí)現(xiàn)一個(gè)全局單例呢,很簡(jiǎn)單,只要保證標(biāo)記的Component在全局只初始化一次即可,比如在Application中初始化,篇幅限制代碼就不貼了,有興趣的騷年可以自己實(shí)踐一下。
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
Android OnCreate()中獲取控件高度與寬度兩種方法詳解
這篇文章主要介紹了Android OnCreate()中獲取控件高度與寬度兩種方法詳解的相關(guān)資料,這里提供了兩種方法,大家可以都看下,需要的朋友可以參考下2016-12-12Android短信驗(yàn)證碼自動(dòng)填寫(xiě)實(shí)現(xiàn)代碼
這篇文章主要為大家詳細(xì)介紹了Android短信驗(yàn)證碼自動(dòng)填寫(xiě)實(shí)現(xiàn)代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-05-05Android FTP服務(wù)器上傳文件攻略(代碼詳解)
這篇文章主要介紹了Android FTP服務(wù)器上傳文件攻略,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-06-06Android使用元數(shù)據(jù)實(shí)現(xiàn)配置信息的傳遞方法詳細(xì)介紹
這篇文章主要介紹了Android使用元數(shù)據(jù)實(shí)現(xiàn)配置信息的傳遞方法,也就是實(shí)現(xiàn)配置快捷菜單功能,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧2022-09-09Android多個(gè)TAB選項(xiàng)卡切換效果
這篇文章主要介紹了Android多個(gè)TAB選項(xiàng)卡切換效果的實(shí)現(xiàn)代碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-04-04基于startActivityForResult方法處理兩個(gè)Activity之間數(shù)據(jù)傳遞問(wèn)題
這篇文章主要介紹了基于startActivityForResult方法處理兩個(gè)Activity之間數(shù)據(jù)傳遞問(wèn)題的相關(guān)資料,需要的朋友可以參考下2015-11-11