アクトインディ開発者ブログ

子供とお出かけ情報「いこーよ」を運営する、アクトインディ株式会社の開発者ブログです

KOINのバージョンが1.0になりました。

すっかり秋めいて来ましたね。
いこーよのAndroidアプリを担当しているhondaです。
前回、KOINの導入をお話をしました。 tech.actindi.net この時に使ったKOINのバージョンは0.9.3でした。
今回、KOINのバージョンが1.0にアップデートされたので(2018/10/26現在の最新バージョンは1.0.1)前回のサンプルコードでのKOIN0.9.3→1.0への以降をしながらKOIN1.0のお話をしたいと思います。

バージョンを変更

build.gradle(app)のKOINバージョンを0.9.3から1.0.1に変更します。
gradle syncすると以下のエラーが表示されます。

f:id:kou_hon:20181026130023p:plain

org.koin:koin-android-architectureがなくなって、Android Architecture ComponentsのViewModelを使用するためには「org.koin:koin-android-viewmodel」を導入する必要があります。
よって、前回の

def koin_version = '0.9.3'
// Koin for Kotlin
implementation "org.koin:koin-core:$koin_version"
// Koin for Android
implementation "org.koin:koin-android:$koin_version"
// Koin for Android Architecture Components
implementation "org.koin:koin-android-architecture:$koin_version"
// Koin for JUnit tests
testImplementation "org.koin:koin-test:$koin_version"


def koin_version = '1.0.1'
// Koin for Kotlin
implementation "org.koin:koin-core:$koin_version"
// Koin for Android
implementation "org.koin:koin-android:$koin_version"
// Koin Android ViewModel feature
implementation "org.koin:koin-android-viewmodel:$koin_version"
// Koin for JUnit tests
testImplementation "org.koin:koin-test:$koin_version"

となり、gradle syncが成功します。

ビルド

gradle syncが完了したのでビルドしてみます。
すると、ビルドエラーが発生します。

f:id:kou_hon:20181026130804p:plain

これはViewModelのinjectionを行うviewModelのパッケージが変更されたためです。

import org.koin.android.architecture.ext.viewModel

から

import org.koin.android.viewmodel.ext.android.viewModel

に変更します。

Deprecated対応

ビルドは通るようになって動くようにはなりました。
が、いくつかのメソッドがDeprecatedになっています。
Deprecatedの対応をしていきましょう。
カスタムApplicationクラスのコードを見るとこうなっています。

f:id:kou_hon:20181026131158p:plain

applicationContext関数はmodule関数に変更、beanメソッドはsingleメソッドに変更になり、よりわかりやすい命名になりました。

val myModule: Module = applicationContext {
    viewModel { MainViewModel(get()) }
    factory { SampleNumCounter(get()) as SampleNumCounterInterface }
    bean { SampleNumDataSource() as SampleNumDataSourceInterface }
}

から

val myModule: Module = module {
    viewModel { MainViewModel(get()) }
    factory { SampleNumCounter(get()) as SampleNumCounterInterface }
    single { SampleNumDataSource() as SampleNumDataSourceInterface }
}

に変更します。
以上で前回のサンプルコードを使ったKOIN0.9.3→1.0への変更対応になります。

AndroidX対応

サンプルコードではandroid.arch.lifecycleのViewModelを使っています。
androidx.lifecycleのViewModelを使う場合はorg.koin:koin-android-viewmodel
ではなくAndroidXに対応した
org.koin:koin-androidx-viewmodel
をbuild.gradleに書いておきます。
これでAndroidXでも安心です。

追加されたパッケージ

KOIN1.0から
koin-android-scope
というパッケージが追加されました。
これはActivity、Fragment、Serviceにインジェクトするオブジェクトをインジェクト先のライフサイクルに合わせて管理するためのパッケージのようです。
下記のコードは公式サイトより抜粋したものです。

基本的な使い方

公式ではMVPアーキテクチャーでのPresenterを例にあげています。

まず、Presenterクラスをmoduleに追加します。
scopeで"scope_id"というIDで追加追加します。

val androidModule = module {
    scope("scope_id") { Presenter() }
}

次にSampleActivityにPresenterクラスをインジェクトします。
getScopeでPresenterのインスタンスが新規で生成され、bindScopeでMyActivityにbindされます
これでPresenterインスタンスはMyActivityのライフサイクルが終了すると破棄されるようになります。

class MyActivity : AppCompatActivity() {

    val presenter : Presenter by inject()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        bindScope(getScope("scope_id"))
    }

※注意:getScopeだとインスタンスがないと例外を吐いてしまうのでgetOrCreateScopeを使ったほうが良いようです。

インスタンスをシェアする使い方

また、koin-android-scopeの用途としてActivity間などでオブジェクトをシェアする使い方も公式より紹介されています。
例としてユーザのセッション情報を保持しているUserSessionクラスをシェアするパターンがあります。
まずModuleにUserSessionクラスを追加します。

module {
    scope("session") { UserSession() }
}

次にユーザセッションを使用したいタイミングで以下のコードでスコープを開始します。

getKoin().createScope("session")

そして、以下のようにUserSessionを使用したいActivityなどでinjectします。
これでMyActivity1とMyActivity2でUserSessionのインスタンスをシェアすることが出来ます。

class MyActivity1 : AppCompatActivity() {
    val userSession : UserSession by inject()
}
class MyActivity2 : AppCompatActivity() {
    val userSession : UserSession by inject()
}

また、Kotlin DSLに沿ってシェアすることも出来ます。 例えば、前述したPresenterでUserSessionを使いたい場合、以下のようにModule定義します。

module {
    scope("session") { UserSession() }
    factory { Presenter(get())}
}

次に以下のようにPresenterクラスの生成時にuserSessionを渡すように書き直します。
これでシェアされているUserSessionのインスタンスがPresenterにinjectされます。

class Presenter(val userSession : UserSession)

最後にユーザセッションを終了させたいためなどにUserSessionのインスタンスを破棄する場合は以下のようにclose処理を呼び出します。

val session = getKoin().getScope("session")
session.close()

まとめ

いかがでしたでしょうか?
バージョン1.0からよりAndroidフレンドリーな使い方が出来るようになりました。
KOINはいいぞ。

最後に

We're Hiring Android Engineers! actindi.net