22. 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 例子來舉例:
如果沒看過的話,可以回上二章節看看喔!
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 提供了 single
跟 factory
二種方法,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
就會找到 MyWood() as Wood
。
as Wood
是不可省略的,因為 MyPrinter
需要的型別是 Wood
,而在 koin 的世界裡,Wood
跟 MyWood
是互不相通的。
有了 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。是不是很簡單呢?
實際上
get
跟inject
是ComponentCallbacks
的extension
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?
Last updated
Was this helpful?