Android MVVM 架構(四)-使用 Koin

Android MVVM 架構(四)-使用 Koin

情境

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 操作。