본문

180911(화) - Koin

Koin


Gradle setup

- RC version 이지만, 9월 중 릴리즈 버전 나온다고 함


// Add Jcenter to your repositories if needed
repositories {
    jcenter()    
}
dependencies {
    // Koin for Kotlin apps
    compile 'org.koin:koin-core:1.0.0-RC-3'
    // Testing
    testCompile 'org.koin:koin-test:1.0.0-RC-3'
}



Component


interface HelloRepository {
    fun giveHello(): String
}

class HelloRepositoryImpl() : HelloRepository {
    override fun giveHello() = "Hello Koin"
}

class MySimplePresenter(val repo: HelloRepository) {

    fun sayHello() = "${repo.giveHello()} from $this"
}



Module

Note : MySimplePresenter class as factory는 new instance each time our Activity will need one.

- single {...} 은 singleton instance 생성


val appModule = module {

    // single instance of HelloRepository
    single<HelloRepository> { HelloRepositoryImpl() }

    // Simple Presenter Factory
    factory { MySimplePresenter(get()) }
}



val helloModule = module {

    single { HelloMessageData() }

    single { HelloServiceImpl(get()) as HelloService }
}


Start Koin

Android

- Application이 꼭 필요한지는 더 알아봐야 함.

- 만약 필요하다면, vertical plugin쪽에서 dagger와 똑같이 문제가 될 수 있음


https://github.com/InsertKoinIO/koin/issues/156

・ 해당 issues를 누군가 제보했고, rework version이 merged됨

(https://github.com/InsertKoinIO/koin/pull/158)


・ https://github.com/InsertKoinIO/koin/pull/158/files/14880cab5f838349848d6d8e95a7b41faf2fbb7a#diff-25e7a3eb9d361258babbefa375e4ec6c


하지만 startKoin(...) 을 중복 사용할 가능성이 있으니 주의 필요.


class MyApplication : Application(){
    override fun onCreate() {
        super.onCreate()
        // Start Koin
        startKoin(this, listOf(appModule))
    }
}


Kotlin

- 특이하게 Application 에서 KoinComponent interface implements

- 마찬가지로 Application 이 필요한지는 의문


/**
 * HelloApplication - Application Class
 * use HelloService
 */
class HelloApplication : KoinComponent {

    // Inject HelloService
    val helloService by inject<HelloService>()

    // display our data
    fun sayHello() = println(helloService.hello())
}


fun main(vararg args: String) {

    startKoin(listOf(helloModule))

    HelloApplication().sayHello()
}



Injecting dependencies

- MySimplePresenter 는 will be created HelloRepository instance

- get() function은 instance를 directly retrieve (non-lazy)


class MySimpleActivity : AppCompatActivity() {

    // Lazy injected MySimplePresenter
    val firstPresenter: MySimplePresenter by inject()

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



AAC & ViewModel

- koin will give a ViewModel to the lifecycle ViewModelFactory and help bind to the current compoent


// Add Jcenter to your repositories if needed
repositories {
    jcenter()    
}
dependencies {
    // Koin for Android - Scope feature
    // include koin-android-scope & koin-android
    compile 'org.koin:koin-android-viewmodel:1.0.0-RC-3'
}



interface HelloRepository {
    fun giveHello(): String
}

class HelloRepositoryImpl() : HelloRepository {
    override fun giveHello() = "Hello Koin"
}



class MyViewModel(val repo : HelloRepository) : ViewModel() {

    fun sayHello() = "${repo.giveHello()} from $this"
}



val appModule = module {

    // single instance of HelloRepository
    single<HelloRepository> { HelloRepositoryImpl() }

    // MyViewModel ViewModel
    viewModel { MyViewModel(get()) }
}



class MyViewModelActivity : AppCompatActivity() {
    
    // Lazy Inject ViewModel
    val myViewModel: MyViewModel by viewModel()

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

        //...
    }
}



Scope

- bindScope(..) : bind current lifecycle. Activity의 lifecycle이 ends되면, close the associated scope and injected instance


// Add Jcenter to your repositories if needed
repositories {
    jcenter()    
}
dependencies {
    // Koin for Android - Scope feature
    // include koin-android
    compile 'org.koin:koin-android-scope:1.0.0-RC-3'
}



interface HelloRepository {
    fun giveHello(): String
}

class HelloRepositoryImpl() : HelloRepository {
    override fun giveHello() = "Hello Koin"
}



class MyScopePresenter(val repo: HelloRepository) {

    fun sayHello() = "${repo.giveHello()} from $this"
}



val appModule = module {

    // single instance of HelloRepository
    single<HelloRepository> { HelloRepositoryImpl() }

    // Scoped MyScopePresenter instance
    scope("session") { MyScopePresenter(get())}
}



class MyScopeActivity : AppCompatActivity() {

    // inject MyScopePresenter from "session" scope 
    val scopePresenter: MyScopePresenter by inject()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_simple)
        
        // bind "session" scope to component lifecycle
        bindScope(getOrCreateScope("session"))

        //...
    }
}



JUnit Tests

- closeKoin() : close Koin context


// Add Jcenter to your repositories if needed
repositories {
    jcenter()
}
dependencies {
    // Koin testing tools
    testcompile 'org.koin:koin-test:1.0.0-RC-3'
}



val helloModule = module {
    single { HelloMessageData() }
    single { HelloServiceImpl(get()) as HelloService }
}



class HelloAppTest : KoinTest() {

    val model by inject<HelloMessageData>()
    val service by inject<HelloService>()

    @Before
    fun before() {
        startKoin(listOf(helloModule))
    }

    @After
    fun after() {
        closeKoin()
    }

    @Test
    fun tesKoinComponents() {
        val helloApp = HelloApplication()
        helloApp.sayHello()

        assertEquals(service, helloApp.helloService)
        assertEquals("Hey, ${model.message}", service.hello())
    }
}


공유

댓글