【Android】Kotlin/KodeinではじめてのDI
Kotlin/Kodeinを用いてはじめてのDIをしてみましょう
初めてDIに触れる人を対象読者としています。Kodeinの詳細な実装については解説しておりません。
DIとはなにか、なにがありがたいのか
DIとはDipendency Injectionの略で、日本語だと依存性注入と言われます。あるクラスAから依存するクラスBがあるとして、その依存関係を外部から指定してあげるものです。
↓Non DIの場合
↓DIの場合
クラスAでは依存先の抽象だけを指定し具象を外部から注入することで、コンポーネント間の依存度が下がり、疎結合なプログラムを構築することができます。
具体例を挙げます。web apiを叩きデータをダウンロードしてくるアプリがある場合、デバッグ中はハードコードした値を使いたいとか、デバッグ用サーバにつなぎたいということがあると思います。
そんなときには外部のほうで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の実装については公式のドキュメントが充実しているので、参考にしてみてください。
ありがとうございました。