Koin — その概要、Dependency Injectionの原則と動作方法

著者: IT Sectr 公開日: 2026-05-04 読了時間: 8 分

Koinは、コード生成、リフレクション、アノテーションなしで動作するKotlin向けDIフレームワークです。このライブラリはDSLを使用してモジュールを記述し、Android、Ktor、Multiplatformをサポートする軽量コンテナを介して依存性を注入します。公式Koinドキュメントによると、このフレームワークはモジュール、スコープ、および最小限のボイラープレートでJetpack Composeの組み込みサポートを提供します。

重要なポイント

  • Koinは、リフレクションやコード生成なしでDSLを使用するKotlin向けDIフレームワークです。
  • モジュールは、single関数とfactory関数による依存性登録の論理的なグループ化です。
  • singleはインスタンスが一度だけ作成されるシングルトンを登録します。
  • factoryはリクエストごとに新しいインスタンスを作成します。
  • スコープは依存関係のライフタイムをActivityなどのコンポーネントにバインドするメカニズムです。

Koinとは何か、その違い

Koinは、リフレクション、アノテーション、コード生成を使用せずに純粋な言語で書かれたKotlin向けDIフレームワークです。コンパイル時にアノテーションプロセッサとコード生成が必要なDagger Hiltとは異なり、Koinはモジュールを記述するための軽量DSLを使用してランタイムでのみ動作します。

Koinの主なアイデアは、複雑な依存関係グラフやコンポーネントツリーの概念を学ぶ必要なく、依存関係を登録および解決するためのシンプルなAPIを提供することです。開発者はコンテナが利用できるクラスを記述し、Koinはコンストラクタまたはby injectによる遅延デリゲートを介して自動的に注入します。このフレームワークはKotlin Multiplatformと完全に互換性があり、Android、iOS、サーバーサイドで統一されたDIアプローチを可能にします。

Kotlin Developers Communityの調査(2025年)によると、Koinは商用Androidプロジェクトの31%で使用されており、Hilt(47%)に次いで2位です。選択される主な理由は、セットアップの容易さとコード生成が不要でプロジェクトのビルドが高速化されることです。

迅速な開発開始が重要な中規模から大規模プロジェクト、またはアーキテクチャ上の理由でHiltが利用できないKotlin MultiplatformソリューションにはKoinを選んでください。

Koinはリフレクションやコード生成を使用しません — すべての登録は、コンパイル時に関数本体に具体的な型を代入するreified型を持つインライン関数に基づいています。これにより、Koinは最終的なAPKサイズの点で最も軽量なDIフレームワークの1つになります。Koinを追加するとアプリケーションサイズがわずか100〜150KB増加するのに対し、Dagger Hiltは生成されたコードにより約500KB増加します。

KoinコンテナとDSLの仕組み

Koinコンテナは、設定付きのラムダを受け取るstartKoin関数を介して初期化されます。このラムダ内で、DIロジックの主要な構成要素である登録付きモジュールが記述されます。

startKoinとモジュール

startKoin関数は、GlobalContextを介してアプリケーションのどこからでもアクセス可能なグローバルコンテナを作成しますが、マルチモジュールプロジェクトでは、分離されたコンテナを作成するためにKoinApplicationを使用することをお勧めします。Androidでは、初期化にAndroidContextが使用され、自動的にApplicationのライフサイクルにバインドされます。モジュールは、Moduleインスタンスのリストを受け入れるmodulesパラメータを介して登録されます。

kotlin
val networkModule = module {
    single {
        OkHttpClient()
    }
    single {
        Retrofit.Builder()
            .baseUrl("https://api.example.com")
            .build()
    }
}

startKoin {
    modules(networkModule)
}

各モジュールには、single(シングルトン)またはfactory(新しいインスタンス)を介した定義が含まれています。定義はget()を介して他の登録済み依存関係を参照でき、明示的な型指定やボイラープレートコードなしで注入グラフを形成します。

DSLとインライン関数

Koinは、コンテキストからの型推論のためにreifiedパラメータを持つインライン関数を積極的に使用します。これにより、クラスを明示的に指定せずに登録を記述できます。single { MyService() } はラムダの戻り値から自動的に型を決定します。

Daggerとは異なり、Koinはコンパイル時に依存関係グラフをチェックしません — すべてのエラーは未解決の依存関係への最初のアクセス時にランタイムで検出されます。これはコードを大幅に簡素化しビルドを高速化するトレードオフですが、DI設定のテストカバレッジが必要です。多くのチームは、コンパイル時チェックがないにもかかわらず、開発の速度とシンプルさのためにKoinを選んでいます。

Koinバージョン3.5では、Koin Annotationsプラグインを介した実験的なコンパイル時グラフチェックが登場しました。開発者は@Moduleと@KoinComponentのアノテーションを追加し、プラグインはビルド時に実行される検証コードを生成します。ただし、Koinの主な利点であるコード生成がないことはこのモードで失われるため、ほとんどのチームはテストによるランタイムチェックを使用した従来のDSLアプローチを引き続き使用しています。

Koinでの依存性注入方法

Koinは、by inject()、get()、およびコンストラクタ直接渡しの複数の依存性注入方法を提供します。選択は使用コンテキストによって異なります。

by inject() — 遅延注入

by injectデリゲートは、AndroidのViewModelやフラグメントで最も一般的な注入方法です。依存関係は遅延的に初期化され、最初のプロパティアクセス時にのみ行われます。これはすぐに必要ではないリソースを多く消費するサービスに効率的です。

kotlin
class MainViewModel : ViewModel() {
    private val repository: UserRepository by inject()

    fun loadUsers() {
        repository.fetchAll()
    }
}

get() — 明示的な取得

get関数は即座に依存関係インスタンスを返します。これは登録時のファクトリラムダ内、または遅延初期化なしで同期的なコンテキストで依存関係が必要な場合に使用されます。by inject()とは異なり、get()は遅延ロードをサポートしておらず、呼び出し時にコンテナがすでに初期化されている必要があります。

Koinのモジュールとスコープ

Koinのスコープは、依存関係のライフタイムをActivity、Fragment、カスタムセッションなどの特定のコンポーネントにバインドするメカニズムです。これはAndroidアプリケーションのメモリ管理における重要な機能です。

scope — コンポーネントへのバインド

モジュール内のscope関数は、バインドされたコンポーネントが存続する限り存続するスコープを作成します。スコープに登録されたすべての依存関係は、スコープが閉じられると破棄され、メモリリークを防ぎます。

kotlin
val userScope = module {
    scope<UserSession> {
        scoped {
            UserRepository(get())
        }
        scoped {
            SessionManager(get())
        }
    }
}

scoped関数は、スコープ内にのみ存在する依存関係を登録します。スコープが閉じられると、すべてのscopedオブジェクトがガベージコレクションの対象になります。

single vs factory

singleは、遅延初期化によりアプリケーション全体で単一のインスタンスを登録します。ネットワーククライアント、キャッシュ、ロガーなどのステートレスサービスに使用されます。

factoryはget()が呼び出されるたびに新しいインスタンスを作成します。ViewModel、リポジトリ、およびアクセスごとに新しいインスタンスが重要なステートフルオブジェクトに適用されます。

AndroidプロジェクトでのKoin

KoinをAndroidプロジェクトに統合するのは最小限です。build.gradleに依存関係を追加し、Application.onCreateでstartKoinを呼び出すだけです。KoinはJetpack Compose、Navigation、WorkManagerとの統合モジュールを提供し、Hiltの本格的な代替手段となっています。

特別なkoin-android-composeライブラリを使用すると、koinViewModel()およびkoinInject()を介してComposable関数に直接依存関係を注入できます。これにより、各画面のパラメータを介してコンテナを渡す必要がなくなり、自動ライフサイクルバインディングによりViewModelコードがよりクリーンになります。

Google I/O 2024によると、Jetpack Composeは新しいAndroidプロジェクトの主要フレームワークになりました。Koinは追加設定なしでComposeのネイティブサポートを提供し、コルーチンコンテキストを考慮してkoinViewModel()を介してスコープをViewModelライフサイクルに自動的にバインドします。

テストのために、KoinはkoinTest関数とkoinTestRule関数を提供し、テストモジュールで分離されたテストコンテナを作成し、テスト完了後に自動的に閉じます。これによりテストの分離が保証され、テストケース間の状態リークが防止されます。

Jetpack NavigationとのKoin統合は、koin-androidx-navigationモジュールを介して実装されます。各画面のViewModelは、画面回転時の状態保存とアプリケーション中断後の復元のためにSavedStateHandleを渡して、by viewModel()を介して自動的に依存関係を受け取ります。

Koinを使用したViewModelのユニットテストには、koin-test-junit5またはkoin-test-junit4ライブラリのkoinTestRuleが使用されます。このルールは各テストの前にテストモジュールで分離されたコンテナを作成し、完了後に自動的に閉じて、テストケース間の状態リークを防止します。実際の依存関係はMockKを介してモックに置き換えられます。single { mockk() } 登録を持つモジュールがメインモジュールをオーバーライドし、テスト内のViewModelは予測可能な依存関係動作を取得します。

Koin 3.xの重要な機能の1つは、Kotlinでサーバーアプリケーションを作成するためのKtorと、デスクトップアプリケーションのためのCompose Multiplatformのサポートです。これにより、Koinは注入パラダイムを変更せずに3つのKotlinプラットフォームすべてをカバーする唯一のDIフレームワークになります。koin-ktorモジュールを使用すると、Applicationブロックでinstall(Koin)を介して依存関係を登録し、Androidと同様にby inject()を介してルートにサービスを注入できます。これにより、Koinはモバイルクライアントからサーバーバックエンドまで、あらゆるアーキテクチャのKotlinプロジェクト向けのユニバーサルDIソリューションになります。

koin-androidx-navigationモジュールを介したJetpack NavigationとのKoin統合により、各画面のViewModelProvider.Factoryを手動で作成する必要がなくなります。マルチモジュールプロジェクトの場合、KoinはloadKoinModulesを介した遅延モジュール読み込みをサポートしており、各フィーチャーモジュールが独立してDI設定を接続できます。

よくある質問

KoinはDagger Hiltとどう違いますか?

Koinはコード生成やアノテーションなしでランタイムで動作し、ビルドを高速化しますが、コンパイル時に依存関係グラフをチェックしません。Hiltはコンパイル時にコードを生成し、DIエラーを早期にキャッチしますが、複雑な設定が必要でビルドが遅くなります。

KoinはKotlin Multiplatformをサポートしていますか?

はい、KoinはKotlin Multiplatformを完全にサポートしています。koin-coreライブラリはすべてのKotlinプラットフォームで動作し、koin-androidとkoin-composeはそれぞれAndroidとiOS向けのプラットフォーム固有の機能を追加します。

Koinで循環依存関係を処理するには?

循環依存関係はランタイムでStackOverflowErrorを引き起こします。Koinは自動的に検出しません。解決策はアーキテクチャのリファクタリングです。共通インターフェースの抽出、Listener/Observerパターンの使用、または遅延初期化を持つファクトリを介したサイクルの解消です。

スコープを手動で閉じる必要がありますか?

Androidでは、スコープをAndroidScopeを介してActivityまたはFragmentのライフサイクルにバインドできます。コンポーネントが破棄されると、Koinは自動的に対応するスコープを閉じます。カスタムスコープ(ユーザーセッション)では、scope.closeを呼び出して手動で閉じます。

Koinでコードをテストするには?

koin-testモジュールのkoinTest関数を使用してください。テストモジュールで分離されたコンテナを作成し、テスト後に自動的に閉じます。実際の依存関係は、MockitoまたはMockKを使用したモジュールを介してモックに置き換えられます。

まとめ

  • Koinは、リフレクション、アノテーション、コード生成なしのDSLを備えたKotlin向けDIフレームワークです。
  • モジュールはsingle(シングルトン)とfactory(新しいインスタンス)を介して登録をグループ化します。
  • startKoinはby inject()とget()を介してアクセス可能なグローバルコンテナを初期化します。
  • スコープは依存関係のライフタイムをAndroidコンポーネントのライフサイクルにバインドします。
  • Composeでの注入はkoinViewModel()とkoinInject()を介して行われます。
  • KoinはKotlin Multiplatformをサポートし、すべてのプラットフォームで統一されたDIアプローチを可能にします。
  • DIエラーはランタイムで検出されるため、設定のテストカバレッジが必須です。

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください