iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 21
0
Mobile Development

Android 十全大補系列 第 21

[Android 十全大補] Dagger as a Pro

Dagger 的強大遠遠超過我們昨天介紹的範疇,如果 dagger 基本教學還無法滿足大家實務上的需求,我們今天就更進一步來繼續討論 dagger 還有什麼特別的功能吧。

如果你還沒看過我們的 dagger 基礎教學,請見:[Android 十全大補] Dagger

Qualifier

第一個可能遇到的問題是要怎麼區別同樣型別的 dependency,假設有以下二種 paper 的提供方式:

@Provides
fun provideMyPaper(wood: Wood): Paper {
    return MyPaper(wood)
}

@Provides
fun provideDoubleAPaper(wood: Wood): Paper {
    return DoubleAPaper(wood)
}

我們有提過 function 名稱並不重要,重點是回傳的 type ,所以如果有任何地方需要 paper 物件,我們要怎麼區別需要的是 MyPaper 還是 DoubleAPaper 呢?

這時候就是 Qualifier 派上用場的地方了,我們這邊使用 Named 這種以 string 為判斷的 Qualifier,回頭幫我們的 providePaperprovideDoubleAPaper 加上 Named annotation 如下:

@Named("normal")
@Provides
fun providePaper(wood: Wood): Paper {
    return MyPaper(wood)
}

@Named("doubleA")
@Provides
fun provideDoubleAPaper(wood: Wood): Paper {
    return DoubleAPaper(wood)
}

然後同樣道理,我們可以在我們的 providePrinter 參數上指定我們所要的 Named

@Provides
fun providePrinter(@Named("doubleA") paper: Paper, ink: Ink): Printer {
    return MyPrinter(paper, ink)
}

這樣就可以輕易的處理多種同型別的不同 dependency,如果覺得 string 不好看的話,也可以試著自己建立 Qualifier,一樣的例子我們可以改成以下的程式碼:

@Qualifier
annotation class DoubleA

@Qualifier
annotation class NormalPaper

@NormalPaper
@Provides
fun providePaper(wood: Wood): Paper {
    return MyPaper(wood)
}

@DoubleA
@Provides
fun provideDoubleAPaper(wood: Wood): Paper {
    return DoubleAPaper(wood)
}

@Provides
fun providePrinter(@DoubleA paper: Paper, ink: Ink): Printer {
    return MyPrinter(paper, ink)
}

有了 DoubleANormalPaper 二個 annotation,我們的程式碼是不是漂亮許多呢?

Scope

學過 Android 的人應該會覺得生命週期是個很重要的主題,對 dagger 來說也是如此,scope 是一個讓我們識別不同生命週期的 annotation,我們可以試著建立一個 scope 如下:

@Scope
annotation class MyScope

接下來試著幫我們的 providePrinter 加上 MyScope 這個 annotation:

@MyScope
@Provides
fun providePrinter(@DoubleA paper: Paper, ink: Ink): Printer {
    return MyPrinter(paper, ink)
}

compile 之後會發現我們也必須將對應的 PrinterComponent 加上一樣的 scope:

@MyScope
@Component(modules = [PrinterModule::class, InkModule::class, PaperModule::class])
interface PrinterComponent {

    fun inject(mainActivity: MainActivity)

}

現在可以成功的 compile 了,但是地球還是繞著太陽轉,其實 scope 只是一個幫助我們識別生命週期的 annotation。

雖然 compiler 會依據這個 annotation 幫我們做對應的檢查,但 MyScope 並沒有其他的額外資訊來描述生命週期的訊息,真正的生命週期是跟著 PrinterComponent 的實體走的。
所以不管你是使用 MyScope 或是 Singleton,都要自己好好管理喔!

Multi-Module

假設今天我們的 PrinterModule 越來越複雜,我們想要獨立出 InkModule 來處理墨水相關的 dependency,在 dagger 要怎麼處理呢?

我們把墨水相關的 @Provides function 都移動到新增的 InkModule 裡如下:

@Module
class InkModule {
    @Provides
    fun provideInk(water: Water): Ink {
        return MyInk(water)
    }

    @Provides
    fun provideWater(): Water {
        return MyWater()
    }
}

回到 PrinterComponent,我們會發現 @Component 的參數 - modules 給了我們線索,這邊是可以放多個 module 的,我們把 InkModule 加到 modules 的清單中:

@Component(modules = [PrinterModule::class, InkModule::class])
interface PrinterComponent {

    fun inject(mainActivity: MainActivity)
}

這樣就完成了,compile 看看是否有任何問題吧!

Multi-component

除了多個 module 間的連結外 Component 之間也可以做連結,假設我們把 InkComonent 也拆分出來成為獨立的檔案:

@Component(modules = [InkModule::class])
interface InkComponent {

    fun getInk(): Ink
}

這樣好處是我們可以用 InkComponent 來限制 InkModule 被外界存取,現在只有 getInk 可以對外,所以外界是無法直接拿到其他諸如 Water 等的 dependency 的。

另一方面,我們必須回頭修改 PrinterComponent 如下:

@Component(dependencies = [InkComponent::class], modules = [PrinterModule::class])
interface PrinterComponent {

    fun inject(mainActivity: MainActivity)
}

我們透過 dependencies 宣告 PrinterComponent 必須依賴於 InkComponent 的存在,感覺上這樣就完成了,不過 compile 之後會在 MainActivity 發現一個錯誤,PrinterComponent 因為有外界 dependency 存在,所以無法使用 DaggerPrinterComponent.create() 來得到 PrinterComponent,我們修改如下:

val inkComponent = DaggerInkComponent.create()
DaggerPrinterComponent
    .builder()
    .inkComponent(inkComponent)
    .build()
    .inject(this)

這樣就完成多個 component 的組成了,是不是很神奇呢!

以上就是今天的內容了,除了我們所分享的內容外,dagger 還可以做更多複雜的操作,module 的 includes,Subcomponent 等都是很有意思的主題,大家可以多多探索,甚至去看 dagger 的 source code 也會讓大家對 dagger 或 annotation processing 更了解喔。

希望 dagger 這把小刀大家會越用越順手!我們明天見!

Android 十全大補已經正式出書上架囉!
有興趣的讀者歡迎參考:
https://www.tenlong.com.tw/products/9789864345786


上一篇
[Android 十全大補] Dagger
下一篇
[Android 十全大補] Koin
系列文
Android 十全大補30

尚未有邦友留言

立即登入留言