使い方
interface Heater
interface Pump
class ElectricHeater : Heater
class Thermosiphon(private val heater: Heater) : Pump
class CoffeeMaker(val heater:Heater, val pump:Pump)
1. dependency
implementation 'org.koin:koin-android:0.6.0'
testImplementation 'org.koin:koin-test:0.6.0'
2. AndroidModule を継承したクラスを用意し、context() メソッドを実装する
この Context は Android の Context ではなくて org.koin.dsl.context.Context です。 Context の applicationContext() を使って構成します。
class DripCoffeeModule : AndroidModule() {
override fun context(): Context {
return applicationContext {
provide { ElectricHeater() } bind Heater::class
provide { Thermosiphon(get()) } bind Pump::class
provide { CoffeeMaker(get(), get()) }
}
}
}
applicationContext() は name として Scope.ROOT を指定した Context を生成します。
fun applicationContext(init: Context.() -> Unit) = Context(Scope.ROOT, koinContext).apply(init)
provide はデフォルトでは singleton になります。singleton にしない場合は isSingleton に false を指定するか、provideFactory を使います。
provide(isSingleton = false) { CoffeeMaker(get(), get()) }
provideFactory { CoffeeMaker(get(), get()) }
provide で name を指定することもできます。name を変えることで、同じ型を返す provide を複数定義できます。
provide("Coffee") { CoffeeMaker(get(), get()) }
provide("Coffee2") { CoffeeMaker(get(), get()) }
context() で sub context を作ることができます。Context には Scope 名を指定することができます。
class DripCoffeeModule : AndroidModule() {
override fun context(): Context {
return applicationContext {
context("MainActivity") {
provide { ElectricHeater() } bind Heater::class
provide { Thermosiphon(get()) } bind Pump::class
provide { CoffeeMaker(get(), get()) }
}
}
}
}
provide するインスタンスを生成するときに Android の Context が必要な場合、androidApplication で Application インスタンスを取得することができます。
provide { ElectricHeater(androidApplication) } bind Heater::class
3. アプリケーションクラスの onCreate() で startKoin() を呼ぶ
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
startKoin(this, listOf(DripCoffeeModule()))
}
}
startKoin() は android.app.Application の拡張関数として定義されています。
4. inject
class MainActivity : Activity() {
val maker by inject<CoffeeMaker>()
}
inject() は
inline fun <reified T> ComponentCallbacks.inject(name: String = "") = lazy { (StandAloneContext.koinContext as KoinContext).get<T>(name) }
なので以下と同じ、つまり lazy です。
class MainActivity : Activity() {
val maker by lazy { (StandAloneContext.koinContext as KoinContext).get<CoffeeMaker>() }
}
lazy が嫌なら onCreate() で (StandAloneContext.koinContext as KoinContext).get() を自分で呼んで代入すればできますが、全部自動で一括でとなるとやはりアノテーションなどが必要ですね。
class MainActivity : Activity() {
private lateinit var maker: CoffeeMaker
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
maker = (StandAloneContext.koinContext as KoinContext).get()
}
}
provide で name を指定した場合、inject にその name を指定して取得できます。
class MainActivity : Activity() {
val maker by inject<CoffeeMaker>("Coffee")
}
Scope 名を指定してインスタンスを解放することができます。
override fun onDestroy() {
super.onDestroy()
releaseContext("MainActivity")
}
ContextAwareActivity を継承して Scope 名を指定すると同じことができます。ContextDropMethod を指定しないときのデフォルトは ContextDropMethod.onPause(onPause() で releaseContext() される)です。
class TestActivity : ContextAwareActivity("MainActivity", ContextDropMethod.OnDestroy) {
val maker by inject<CoffeeMaker>()
}
おまけ、Daggerの場合
kapt 'com.google.dagger:dagger-compiler:2.11'
implementation 'com.google.dagger:dagger:2.11'
interface Heater
interface Pump
class ElectricHeater : Heater
class Thermosiphon @Inject constructor(private val heater: Heater) : Pump
class CoffeeMaker @Inject constructor(val heater: Heater, val pump: Pump)
@Module
class DripCoffeeModule {
@Provides
fun provideHeater(): Heater {
return ElectricHeater()
}
@Provides
fun providePump(pump: Thermosiphon): Pump {
return pump
}
}
@Component(modules = arrayOf(DripCoffeeModule::class))
interface CoffeeShop {
fun maker(): CoffeeMaker
}
val coffeeShop = DaggerCoffeeShop.builder()
.dripCoffeeModule(DripCoffeeModule())
.build()
val maker = coffeeShop.maker()
assertThat(maker.heater).isNotNull()
assertThat(maker.pump).isNotNull()