【Android】Kotlin/KodeinではじめてのDI

Kotlin/Kodeinを用いてはじめてのDIをしてみましょう

初めてDIに触れる人を対象読者としています。Kodeinの詳細な実装については解説しておりません。

DIとはなにか、なにがありがたいのか

DIとはDipendency Injectionの略で、日本語だと依存性注入と言われます。あるクラスAから依存するクラスBがあるとして、その依存関係を外部から指定してあげるものです。

↓Non DIの場合

f:id:shimbaroid:20170611005502p:plain

↓DIの場合

f:id:shimbaroid:20170611005533p:plain

クラスAでは依存先の抽象だけを指定し具象を外部から注入することで、コンポーネント間の依存度が下がり、疎結合なプログラムを構築することができます。

具体例を挙げます。web apiを叩きデータをダウンロードしてくるアプリがある場合、デバッグ中はハードコードした値を使いたいとか、デバッグ用サーバにつなぎたいということがあると思います。

f:id:shimbaroid:20170611005549p:plain

そんなときには外部のほうでbuild flavorを見るなどして注入する依存を切り替えます。

ソースコードデバッグビルド・リリースビルドで切り替える処理が散るのを防ぐことができたり、テストに対応しやすくできたりします。

Kotlin/Kodeinで最速DIする

それでは、Kotlin/Kodeinで最速でDIしてみましょう。Kodeinの使い方の解説を主眼とします。

環境

Android Studio 3.0 Canary 2 Kotlin 1.1.2-4 Kodein 4.0.0

Kodein導入

dependencies {
    ~
    compile 'com.github.salomonbrys.kodein:kodein:4.0.0'
    compile 'com.github.salomonbrys.kodein:kodein-android:4.0.0'
}

Stringを注入してみる

例えば"Hello, Kodein!“とログに吐く処理があるとして、この文字列を外部から注入するようにしてみます。

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        Log.d("---", "Hello, Kodien!")
    }
}

さてこれまで外部外部と言ってきましたが、今回であれば「MainActivity以外」ですね。ということでApplicationクラスのサブクラスにしてみます。ライフサイクル的に先行する部分にしておきます。

class MyApplication : Application(), KodeinAware {

    override val kodein = Kodein {
        bind<String>() with instance("Hello, Kodein!")
    }
}

Kodeinが登場しました。

Applicationクラスを継承しつつKodeinAwareを実装し、val kodeinをオーバーライド、セットアップします。ここで文字列をバインド(注入するインスタンスとして宣言)します。

ではバインドした"Hello, Kodien!“を取り出す実装をMainActivityに施します。

class MainActivity : KodeinAppCompatActivity() {

    val greeting: String by instance()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        Log.d("---", greeting)
    }
}

まず継承元をKodeinAppCompatActivityもしくはKodeinActivityにします。これはActivityでKodeinを扱いやすくするラッパーです。

次に文字列の注入ですが、val greeting: String by instance()がそれにあたります。これでMyApplicationにてバインドした文字列がgreetingへ代入されます。

実行してみると、ログに"Hello, Kodein!“が正しく表示されることが確認できると思います。また"Hello, Kodein!"を別の文字に変えてもログが連動して変わると思います。

Retrofit.Serviceを注入してみる

もう少し実用的な、build flavorをみて依存を切り替える例を挙げます。 Moduleという仕組みを使うことで、バインドをまとめることができます。

interface GithubService {

    @GET("/repositories")
    fun get(): Observable<List<Repository>>
}
class TestGithubService : GithubService {
    override fun get(): Observable<List<Repository>> = Observable.just(
            listOf(Repository("Repo-1"), Repository("Repo-2"), Repository("Repo-3"))
    )
}

val debugModule = Kodein.Module {
    bind<GithubService>() with instance(TestGithubService())
}
val releaseModule = Kodein.Module {
    bind<GithubService>() with instance(Retrofit.Builder()
            .baseUrl("https://api.github.com/")
            .addConverterFactory(GsonConverterFactory.create())
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .build()
            .create(GithubService::class.java))
}
class MyApplication : Application(), KodeinAware {

    override val kodein by Kodein.lazy {
        when (BuildConfig.FLAVOR) {
            "debug" -> import(debugModule)
            "release" -> import(releaseModule)
        }
    }
}
class MainActivity : KodeinAppCompatActivity() {

    val githubService: GithubService by instance()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        githubService.get()
                .map { repository ->
                    repository.map { (name) ->
                        name
                    }
                }
                .subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe { strings ->
                    (findViewById(R.id.list) as ListView).adapter =
                            ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, strings)
                }
    }
}

以上です

Kotlin/Kodeinを用いて簡単にDIの概要解説とサンプルの紹介をしました。

Kodeinの実装については公式のドキュメントが充実しているので、参考にしてみてください。

ありがとうございました。

参考

Kodein

猿でも分かる! Dependency Injection: 依存性の注入

Dagger 2 から Kotlin製DIコンテナ Kodein へ乗り換える