定義
使用 Koin,您可以在模組中描述定義。本節將探討如何宣告、組織與連結您的模組。
撰寫模組
Koin 模組是您宣告所有元件的空間。使用 module
函式來宣告 Koin 模組:
val myModule = module {
// your dependencies here
}
在此模組中,您可以按照下方描述來宣告元件。
定義單例
宣告單例元件表示 Koin 容器將保留您所宣告元件的唯一實例。在模組中使用 single
函式來宣告單例:
class MyService()
val myModule = module {
// declare single instance for MyService class
single { MyService() }
}
在 Lambda 運算式中定義您的元件
single
、factory
和 scoped
關鍵字協助您透過 Lambda 運算式來宣告您的元件。此 Lambda 描述了您建構元件的方式。通常我們透過建構函式實例化元件,但您也可以使用任何運算式。
single { Class constructor // Kotlin expression }
您的 Lambda 的結果型別是您元件的主要型別。
定義 Factory
Factory 元件宣告是一種定義,每次您請求此定義時,它都會提供您一個新實例(此實例不會被 Koin 容器保留,因為它稍後不會將此實例注入其他定義中)。使用帶有 Lambda 運算式的 factory
函式來建構元件。
class Controller()
val myModule = module {
// declare factory instance for Controller class
factory { Controller() }
}
INFO
Koin 容器不會保留 Factory 實例,因為每次請求定義時它都會提供一個新實例。
解析與注入依賴項
現在我們可以宣告元件定義了,我們希望透過依賴注入來連結實例。要在 Koin 模組中解析實例,只需使用 get()
函式來取得所需的元件實例。此 get()
函式通常用於建構函式中,以注入建構函式值。
INFO
要使用 Koin 容器進行依賴注入,我們必須以建構函式注入的風格來撰寫:在類別建構函式中解析依賴項。如此一來,您的實例將會以來自 Koin 的注入實例來建立。
讓我們以幾個類別為例:
// Presenter <- Service
class Service()
class Controller(val view : View)
val myModule = module {
// declare Service as single instance
single { Service() }
// declare Controller as single instance, resolving View instance with get()
single { Controller(get()) }
}
定義:綁定介面
single
或 factory
定義使用其給定 Lambda 定義的型別,即:single { T }
。該定義的匹配型別是此運算式中唯一匹配的型別。
讓我們以一個類別和已實作介面為例:
// Service interface
interface Service{
fun doSomething()
}
// Service Implementation
class ServiceImp() : Service {
fun doSomething() { ... }
}
在 Koin 模組中,我們可以使用 Kotlin 的 as
轉換運算子,如下所示:
val myModule = module {
// Will match type ServiceImp only
single { ServiceImp() }
// Will match type Service only
single { ServiceImp() as Service }
}
您也可以使用推斷型別運算式:
val myModule = module {
// Will match type ServiceImp only
single { ServiceImp() }
// Will match type Service only
single<Service> { ServiceImp() }
}
NOTE
這種第二種宣告方式是首選,並將在本文檔的其餘部分中使用。
額外型別綁定
在某些情況下,我們希望從一個定義中匹配多種型別。
讓我們以一個類別和介面為例:
// Service interface
interface Service{
fun doSomething()
}
// Service Implementation
class ServiceImp() : Service{
fun doSomething() { ... }
}
要使定義綁定額外型別,我們使用帶有類別的 bind
運算子:
val myModule = module {
// Will match types ServiceImp & Service
single { ServiceImp() } bind Service::class
}
請注意,這裡我們將直接使用 get()
解析 Service
型別。但如果我們有多個綁定 Service
的定義,我們就必須使用 bind<>()
函式。
定義:命名與預設綁定
您可以為您的定義指定一個名稱,以幫助您區分兩個相同型別的定義:
只需使用其名稱請求您的定義:
val myModule = module {
single<Service>(named("default")) { ServiceImpl() }
single<Service>(named("test")) { ServiceImpl() }
}
val service : Service by inject(qualifier = named("default"))
get()
和 by inject()
函式允許您在需要時指定定義名稱。此名稱是由 named()
函式產生的 qualifier
。
預設情況下,如果型別已經綁定到一個定義,Koin 將透過其型別或名稱來綁定定義。
val myModule = module {
single<Service> { ServiceImpl1() }
single<Service>(named("test")) { ServiceImpl2() }
}
然後:
val service : Service by inject()
將觸發ServiceImpl1
定義val service : Service by inject(named("test"))
將觸發ServiceImpl2
定義
宣告注入參數
在任何定義中,您都可以使用注入參數:這些參數將由您的定義注入並使用:
class Presenter(val view : View)
val myModule = module {
single{ (view : View) -> Presenter(view) }
}
與已解析的依賴項(使用 get()
解析)相反,注入參數是透過解析 API 傳遞的參數。這表示這些參數是透過 get()
和 by inject()
並搭配 parametersOf
函式傳遞的值:
val presenter : Presenter by inject { parametersOf(view) }
更多資訊請參閱注入參數章節
定義終止 - OnClose
您可以使用 onClose
函式,在定義上添加一個回調,一旦呼叫定義關閉:
class Presenter(val view : View)
val myModule = module {
factory { (view : View) -> Presenter(view) } onClose { // closing callback - it is Presenter }
}
使用定義旗標
Koin DSL 也提供了一些旗標。
在啟動時建立實例
定義或模組可以被標記為 CreatedAtStart
,以便在啟動時(或您需要時)建立。首先在您的模組或定義上設定 createdAtStart
旗標。
定義上的 CreatedAtStart 旗標
val myModuleA = module {
single<Service> { ServiceImp() }
}
val myModuleB = module {
// eager creation for this definition
single<Service>(createdAtStart=true) { TestServiceImp() }
}
模組上的 CreatedAtStart 旗標:
val myModuleA = module {
single<Service> { ServiceImp() }
}
val myModuleB = module(createdAtStart=true) {
single<Service>{ TestServiceImp() }
}
startKoin
函式將自動建立被 createdAtStart
標記的定義實例。
// Start Koin modules
startKoin {
modules(myModuleA,myModuleB)
}
INFO
如果您需要在特定時間(例如在背景執行緒而非 UI)載入某些定義,只需取得/注入所需的元件即可。
處理泛型
Koin 定義不考慮泛型型別參數。例如,下面的模組嘗試定義兩個 List 的定義:
module {
single { ArrayList<Int>() }
single { ArrayList<String>() }
}
Koin 不會以這樣的定義啟動,它會認為您想要覆寫其中一個定義。
為了允許您使用這兩個定義,您必須透過它們的名稱或位置(模組)來區分它們。例如:
module {
single(named("Ints")) { ArrayList<Int>() }
single(named("Strings")) { ArrayList<String>() }
}