iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 22
0
Mobile Development

Android 十全大補系列 第 22

[Android 十全大補] Koin

Dagger 的強大跟好處相信大家可以慢慢能體會到,但實務上有時候我們不需要這麼強大的功能,或者是你覺得 dagger 學習曲線太陡了,這時候 koin 可能是另一個好選擇。

https://github.com/InsertKoinIO/koin

Koin

Koin 運用了大量的 kotlin 特性,讓 dependency injection 變成非常符合 kotlin 的風格,使用上也十分簡單。

對 koin 用到的特性有興趣的話,請參考:
Function literals with receiver: https://kotlinlang.org/docs/reference/lambdas.html#function-literals-with-receiver
Extensions: https://kotlinlang.org/docs/reference/extensions.html
Delegated Properties: https://kotlinlang.org/docs/reference/delegated-properties.html
Reified type parameters: https://kotlinlang.org/docs/reference/inline-functions.html

而效能跟其他相似 project 的 performance 比較起來也是蠻不錯的,一般使用可能不會有太大差別:

資料來源:https://medium.com/koin-developers/ready-for-koin-2-0-2722ab59cac3
可以看到 dagger setup 真的是超快的,因為使用 annotation processing,所有耗時的工作都在 compile 時做完了。

我們就一起來看 koin 有多麽簡潔優雅吧!

Install

跟以往一樣我們需要宣告 dependency 在 build.gradle

dependencies {
    implementation "org.koin:koin-core:2.0.1"
    implementation "org.koin:koin-android:2.0.1"
}

Usage

我們一樣拿我們之前的 Printer 例子來舉例:

如果沒看過的話,請見:[Android 十全大補] Dagger

interface Wood

interface Water

interface Ink

interface Paper

interface Printer

class MyWood : Wood

class MyWater : Water

class MyInk(val water: Water) : Ink

class MyPaper(val wood: Wood) : Paper

class MyPrinter(val paper: Paper, val ink: Ink) : Printer

我們建立一個 module 來宣告所有可提供的 dependency,koin 提供了 singlefactory 二種方法,single 保證每次拿到的物件都是同一個,而 factory 則是每次呼叫都會有個新的物件。

val myModule = module {
    factory { MyPrinter(get(), get()) as Printer }
    single { MyWood() as Wood }
    single { MyWater() as Water }
    single { MyInk(get()) as Ink }
    single { MyPaper(get()) as Paper }
}

get 是個 reified type function,會自動轉換成需要的型別。
比如說 MyPrinter(get(), get()) 第一個 get 回傳的是 Paper 型別、而第二個 get 則會是 Ink 型別。

get 的值則會依照需要的型別找到相對應的建構 function,比如說 MyPrinter 的第一個 get 就會找到 MyPaper() as Paper

as Paper 是不可省略的,因為 MyPrinter 需要的型別是 Paper,而在 koin 的世界裡,PaperMyPaper 是互不相通的。

有了 modules 之後,在我們的 Andoid Application 啟動 koin 如下程式碼:

class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        startKoin {
            modules(myModule)
        }
    }
}

最後在 Activity 或是任何 context base 的 class 就可以用以下方式拿到 dependency 囉:

class MainActivity : AppCompatActivity() {

    val printer: Printer by inject()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val printer: Printer = get()

我們可以直接透過萬用的 get 直接拿到相對應型別的 dependency,或是使用 by inject() 做 lazy loading。是不是很簡單呢?

實際上 getinjectComponentCallbacksextension function,所以不只 context base,包含 Fragment 也可以使用喔!

挑戰:

  1. 請試著使用 Qualifier 解決同型別衝突
  2. 請把 PrinterModule 拆成多個 module

Pros & Cons

Koin 跟 dagger 比較起來的話,有以下幾個優點:

  • Fast compile
  • Easy to learn
  • Pure kotlin

而缺點的話:

  • Slow at setup
  • May cause RuntimeException

取捨就靠大家的智慧了,不知道大家比較喜歡哪個 library?

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


上一篇
[Android 十全大補] Dagger as a Pro
下一篇
[Android 十全大補] MVVM
系列文
Android 十全大補30

1 則留言

1
snowdaily
iT邦新手 5 級 ‧ 2020-12-22 12:59:33

而 get 的值則會依照需要的型別找到相對應的建構 function,比如說 MyPrinter 的第一個 get 就會找到 MyWood() as Wood。

as Wood 是不可省略的,因為 MyPrinter 需要的型別是 Wood,而在 koin 的世界裡,Wood 跟 MyWood 是互不相通的。


以上 MyPrinter 應該是 MyPaper?

我要留言

立即登入留言