情境
Dependency Injection 是一種優化程式架構的機制,如果要了解 DI(注入) 就需要先理解依賴反轉的概念,DI 是一種很方便的工具,它可以讓你不需要理會怎麼初始化一個物件,只需要透過簡單的方式就可以直接讓物件生成,即便物件後續有所調整,也不會影響我們既有的邏輯。
Koin 官方網站有很詳細的解釋跟說明,不過上面的教學可能需要再更新一下,如果您需要更新的資訊,可能需要到他的官方 GitHub 上參考。
Koin 是一套非常簡易操作的注入式工具,相較於 Google 官方出的 Dagger 來進行比較的話,個人是推薦使用 Koin 來操作,從網路上可以看到很多 Dagger 難以理解的說法,如果你有對於這兩套工具進行實作的話,相信會有很深刻的體會。
完整程式碼
如果需要完整的程式碼,可以到 GitHub 上觀看或者下載。
程式碼說明
那我們就來把前面 Android MVVM 架構(三)-使用 RxJava 內的範例修改成 Koin 版本吧!
一開始導入 Koin library,你會發現它的導入有夠多:
Core features
// Koin for Kotlin
implementation "org.koin:koin-core:$koin_version"
// Koin extended & experimental features
implementation "org.koin:koin-core-ext:$koin_version"
// Koin for Unit tests
testImplementation "org.koin:koin-test:$koin_version"
// Koin for Java developers
implementation "org.koin:koin-java:$koin_version"
Android
// Koin for Android
implementation "org.koin:koin-android:$koin_version"
// Koin Android Scope features
implementation "org.koin:koin-android-scope:$koin_version"
// Koin Android ViewModel features
implementation "org.koin:koin-android-viewmodel:$koin_version"
// Koin Android Experimental features
implementation "org.koin:koin-android-ext:$koin_version"
AndroidX
// Koin AndroidX Scope features
implementation "org.koin:koin-androidx-scope:$koin_version"
// Koin AndroidX ViewModel features
implementation "org.koin:koin-androidx-viewmodel:$koin_version"
// Koin AndroidX Experimental features
implementation "org.koin:koin-androidx-ext:$koin_version"
Ktor
// Koin for Ktor Kotlin
implementation "org.koin:koin-ktor:$koin_version"
但實際上我們其實拿我們需要的函式庫即可,像我們這次的範例其實只是需要將 ViewModel 進行替換而已,因此不需要將所有的函式庫全部導入,以下就是這次範例所使用的。
def koin_version = '2.1.0-alpha-1'
// Koin for Kotlin
implementation "org.koin:koin-core:$koin_version"
// Koin AndroidX ViewModel features
implementation "org.koin:koin-androidx-viewmodel:$koin_version"
// Koin for Android
implementation "org.koin:koin-android:$koin_version"
那目前 Koin 版本已經出到 2.1.0-alpha-1,所以將版號獨立出來方便以後升版不需要調整後面一堆 library,所以在前面定義一個 koin-version 的變數。
一開始建立一個 class 叫做 MyModule,這裡面是用來宣告定義我們的 ViewModel,告訴 Koin 之後我們初始化 ViewModel 就從這邊來進行提取。
val myModule = module {
viewModel { InfoViewModel(get()) }
}
val repoModule = module {
single { InfoRepository() }
}
這裡就宣告了我們的 ViewModel 以及 Repository,讓他們能夠透過程式自行生成,這樣一來我們就可以把一大串的 ViewModel 初始化進行簡化。
接著我們在 Mainifest 裡面增加一個 Application 的宣告,定義了 MyApplication,因為 Koin 需要做一些初始化的設定。
<application
android:name=".MyApplication"
//...
/>
接著到我們的 MyApplication 裡面的 OnCreate 最下方加入以下程式。
startKoin {
// Android context
androidContext(this@MyApplication)
// modules
val list = listOf(myModule, repoModule)
modules(list)
}
最後我們將原本的 ViewModel 宣告
private lateinit var infoViewModel: InfoViewModel
private lateinit var infoFactory: InfoFactory
private lateinit var infoRepository: InfoRepository
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
infoRepository = InfoRepository()
infoFactory = InfoFactory(infoRepository)
infoViewModel = ViewModelProviders.of(this, infoFactory).get(InfoViewModel::class.java)
}
換成以下樣子。
private val infoViewModel: InfoViewModel by viewModel()
天啊!就這麼簡單喔?沒錯你沒看錯。
這樣就是簡單的 Koin 操作。