Kotlin用Dependency InjectionライブラリのKoinのV3がリリースされています。この記事ではKoin V3をAndroidプロジェクトで使うための基本的な方法を説明します。
目次
Version 3では何が変わった?
公式Blogによると、Koin V3では内部の処理にKotlin Maltiplatformが使われるようになってたり、Jetpack Composeに対応していたりするようです。前者はAndroidアプリだけを開発している分にはとくに影響はなさそうですが、後者は今後のAndroidアプリの開発では必要な場面も増えてきそうです。いまV2で開発しているアプリを無理にV3に移行する必要はないかもしれませんが、新しく開発するアプリではV3を採用しておいた方が無難な気がします。
それでは、Koin V3をAndroidアプリで使うための手順を見ていきます。各サンプルコードには、必要なimport
文も記述しています。Koinの公式ドキュメントにはこの説明がなく、たまにAndroid Studioの自動補完も効かないことがあって、何をimport
すればよいのか悩むことが時々あるので、この記事ではimport
文の記述で悩まなくても済むようにしました。
build.gradleの編集
はじめにKoinの依存関係をgradleに追加します。簡単な使い方をするだけであれば下記のように1行追加するだけでよいです。
dependencies {
implementation "io.insert-koin:koin-android:3.1.2"
}
Koinの最新バージョン番号を確認するには、GitHubのReleaseタグを確認するのがよさそうです。2021年10月17日現在、V3.1.2が最新バージョンです。
koin-android
モジュールはKoinを使う上で必須のモジュールです。V3ではこのモジュールにScopeやViewModelの機能も取り込まれています。従来V2では、ScopeやViewModelに関する機能を使うには、必要なモジュールの依存関係を別途追加する必要がありましたが、V3ではkoin-android
モジュールだけ追加すれば、ScopeやViewModelの機能が利用可能です。Androidアプリ開発でKoinを利用する場合は、ViewModelの注入もKoinを使うことが多いと思うので、この変更は実際のユースケースに即した変更だと思います。
KoinのV3.1.xへの移行ガイドによると、下記の4つのモジュールの依存関係を追加するように書かれています。ですが、今回の例で説明するような基本的な使い方だけしかしないのであれば、一つ目のkoin-android
モジュールだけ追加すればOKです。
dependencies {
def koin_version = "3.1.2"
// Koin main features for Android (Scope,ViewModel ...)
implementation "io.insert-koin:koin-android:$koin_version"
// Koin Java Compatibility
implementation "io.insert-koin:koin-android-compat:$koin_version"
// Koin for Jetpack WorkManager
implementation "io.insert-koin:koin-androidx-workmanager:$koin_version"
// Koin for Jetpack Compose
implementation "io.insert-koin:koin-androidx-compose:$koin_version"
}
二つ目、koin-android-compat
モジュールは、
Java Compat API has been extracted in
https://insert-koin.io/docs/migration/migrate#android-moduleskoin-android-compat
module
とのことなので、おそらくJavaでKoinを使う場合に必要なのだと思います。
三つ目、四つ目はそれぞれ、Jetpack WorkManager, Jetpack Composeのコード中でKoinを使ってDIする際に必要になります。
ModelとViewModelの定義
MVVMにおけるModelとViewModelは、下記のように定義されているとします。DIでテストを効率的に行う、などということを考える場合は、ModelはInterfaceで定義し、実際のモデルやテスト用のモデルのクラスにInterfaceを実装する、というような手順を踏むのが正しいと思います。ですが単に、ModelをSingletonで扱うためにDIを導入するケースもあるでしょう。今回はKoinを使うときになるべく何も考えずに使えるテンプレート的なサンプルとしての紹介ですので、Interfaceなどは作成せず、直接Modelクラスを作成しています。
class MyModel {
...
}
class MyViewModel(model: MyModel): ViewModel() {
...
}
Koinモジュール作成
Koinの「モジュール」は概念をうまく説明するのが難しいです。「いくつかのオブジェクトのインスタンスの作成方法について記述したもの」というくらいの理解で、間違ってはいないと思います。
ここでは上で定義したModelとViewModelについて記述します。記述場所はどこでもよいのですが、私は次に定義する独自Applicationクラスのファイル内に定義するようにしています。
import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.module
val appModule = module {
single { MyModel() }
viewModel { MyViewModel(get()) }
}
この例ではMyModel
クラスをSingletonで扱うため、single
キーワードで定義しています。ViewModelクラスの定義には、viewModel
キーワードを使います。MyViewModel
クラスのコンストラクタの引数は、Model
クラスです。同じモジュール内で定義しているクラスのインスタンスは、get()
で注入できます。
Koinを開始
Applicationクラスを継承したクラスを作成し、onCreate()
からstartKoin
を呼び出し、Koinを開始します。
import android.app.Application
import org.koin.android.ext.koin.androidContext
import org.koin.core.context.startKoin
class MyApplication: Application() {
override fun onCreate() {
super.onCreate()
startKoin {
//androidContext(this@ViewOnlyViewerApp)
modules(appModule)
}
}
}
modules()
を呼び出し、上で定義したモジュールを開始します。
androidContext()
は、モジュール内でContextを参照したい場合に呼び出します。今回の例では不要です。
MyApplication
クラスを定義しただけでは、このクラスが使われないので注意します。このクラスを使うには、AndroidManifest
に記述を追加します。
<application
android:name=".ViewOnlyViewerApp"
...
</application>
ViewModelインスタンスの注入
これで準備は整いました。最後に、実際にViewModelを使う部分でインスタンスを注入します。
import org.koin.androidx.viewmodel.ext.android.viewModel
class MainActivity : ComponentActivity() {
val viewModel: MainViewModel by viewModel()
...
}
by viewModel()
はlazyなので、viewModel
変数に初めてアクセスしたタイミングで注入されます。インスタンスの作成は、モジュールに記述した内容で実施されます。したがって、この時にMyModel
クラスのインスタンスも注入されます。MyModel
クラスはシングルトンで定義しているので、すでにインスタンスが存在していれば同じインスタンスが注入され、まだ作成されていなければ作成されます。
まとめ
以上がKoin V3の最もシンプルな使い方です。使い始めで悩まなくて済むよう、なるべく簡潔に書きました。テンプレート的にご利用ください。
個人的には、シングルトンオブジェクトを簡単に扱うことができる、というだけでもKoin(をはじめとするDIライブラリ)は利用価値があると思っています。定型的な記述はなるべく楽して済ませ、アプリのUIやロジックの実装に集中したいものです。