Kotlin 中的依賴注入 KODEIN

用 Java 進行 Android 開發的話,依賴注入這一塊通常使用 Dagger ,轉用 Kotlin 以後有更多的選擇,Kodein 是個不錯的庫, 4.0 5.0 都使用過了,相對於 Dagger 有不少好處。android

  • 用 Kotlin 編寫利用了更優秀的語言特性 好比類型推斷,Dagger 在編寫 Component 的時候須要知道注入類的類型
  • 無需像 Dagger 同樣編寫大量模板代碼
  • 不會像 Dagger 同樣在編譯期由於其它的編譯錯誤致使沒法生成所需文件從而報一堆錯。
  • ...

最基本的使用步驟

  1. 在 Application 繼承 KodeinAware 並綁定依賴
class MyApp : Application(), KodeinAware {
	override val kodein = Kodein.lazy { 
	    /* bindings */
	}
}
複製代碼
  1. 在 context aware 的 Android 類中經過 closestKodein 方法獲取
  2. 加載依賴
class MyApp : Application(), KodeinAware {

    //實例化 Application 級的 kodein 經過 DSL 綁定 module
	override val kodein = Kodein.lazy { 
        //導入預設的 android 組件
        import(androidModule(this@MainApplication))
        //綁定或者導入自定義依賴
	}
}
複製代碼

相關基本概念

在 Application 定義 Application 級的 Kodein

class MyApp : Application(), KodeinAware {

    //實例化 Application 級的 kodein 經過 DSL 綁定 module
	override val kodein = Kodein.lazy { 
        //導入預設的 android 組件
        import(androidModule(this@MainApplication))
        //綁定或者導入自定義依賴
	}
}
複製代碼

經過 closestKodein 恢復 Application 級的 Kodein 而後經過 Kodein 加載依賴

kodein & ds 默認都是懶加載
class MyActivity : Activity(), KodeinAware {
    override val kodein by closestKodein()
    val ds: DataSource by instance()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ds.connect() 
        /* ... */
    }
}
複製代碼

使用 Trigger 在 onCreate() 中手動觸發加載依賴(取消懶加載)

一樣能夠避免依賴死循環(除非加載依賴的方式只有 instance)bash

class MyActivity : Activity(), KodeinAware {
    override val kodein by closestKodein()
    override val kodeinTrigger = KodeinTrigger()
    val ds: DataSource by instance()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        kodeinTrigger.trigger() 
        /* ... */
    }
}
複製代碼

在沒有 Non-Context-Aware 的類中加載 Kodein

class MyController(androidContext: Context) : KodeinAware {
    override val kodein by androidContext.closestKodein()
    override val kodeinContext = kcontext(androidContext)
    val inflater: LayoutInflater by instance()
}
複製代碼

多級 Kodin 依賴

定義 Activity 級的 Kodein 繼承自 Application 級
class MyActivity : Activity(), KodeinAware {
    private val _parentKodein by closestKodein() 
    override val kodein: Kodein = Kodein {
        extend(_parentKodein) 
        /* activity specific bindings */
    }
}
複製代碼

Activity Retained Kodein

使用 retainedKodein 在 Activity 重啓的時候不會從新建立 Kodein
class MyActivity : Activity(), KodeinAware {
    private val _parentKodein by closestKodein()
    override val kodein: Kodein by retainedKodein { 
        extend(_parentKodein)
        /* activity specific bindings */
    }
}
複製代碼

Android scopes 做用域

//每一個 Activity 一個單例
val kodein = Kodein {
    bind<Controller>() with scoped(androidScope<Activity>()).singleton { ControllerImpl(context) } 
}
複製代碼

Activity retained scope

一樣 activityRetainedScope 在 Activity 重啓時不會從新建立依賴

val kodein = Kodein {
    bind<Controller>() with scoped(activityRetainedScope).singleton { ControllerImpl() } 
}
複製代碼

Bindings: Declaring dependencies 申明依賴的一些重要的參數

綁定的方式
  • Provider binding : 每次加載都生成新的實例,無參, 傳入() -> T
  • Singleton binding : 單例 傳入 () → T
  • Eager singleton : 單例 建立了 Kodein 實例後當即實例化 傳入 () -> T
  • Factory binding : 每次加載都生成新的實例,須要參數,傳入(A) -> T
  • Multiton binding : 有參的單例,一樣參數一樣實例,傳入 (A) -> T
  • Tagged bindings : 經過 tag 來區分同類型不一樣的實例 例如
val kodein = Kodein {
    bind<Die>() with ... 
    bind<Die>(tag = "DnD10") with ... 
    bind<Die>(tag = "DnD20") with ... 
}
複製代碼
基本上以上的參數涵蓋了大部分的通用使用場景,Kodein 還有不少複雜的高級用法

JVM: Soft & Weak 兩種引用回收機制

  1. 使用 WeakReference 在OutOfMemoryException以前 JVM 執行 GC
  2. 使用 SoftReference 在沒有引用的時候就 JVM 執行 GC
val kodein = Kodein {
    bind<Map>() with refSingleton(ref = softReference) { WorldMap() } 
    bind<Client>() with refSingleton(ref = weakReference) { id -> clientFromDB(id) } 
}
複製代碼

Transitive dependencies 依賴傳遞

依賴中使用依賴的狀況,Kotlin 的類型推斷系統能夠很簡單的實現。
class Die(private val random: Random, private val sides: Int) {
/*...*/
}

val kodein = Kodein {
    bind<Die>() with singleton { Die(instance(), instance(tag = "max")) } 

    bind<Random>() with provider { SecureRandom() } 
    constant(tag "max") with 5 
}
複製代碼
更多高級用法...
相關文章
相關標籤/搜索