Kokain is pragmatic lightweight dependency injection framework for Kotlin based Applications. The Framework is reduced to the core functionalities of DI. Therefore the setup is pretty easy and producing nearly zero overhead.
-
easy to use - just annotate beans and start the framework on application start
-
no declaration of instance creation
-
possibility to safely inject activity context if bean injected by a activity hierarchy
-
different lifecycle scopes
-
lightweight implementation
-
pragmatic api
-
inject android context in a lifecycle aware matter
-
inject systemservices
- Add it in your root build.gradle at the end of repositories:
buildscript {
repositories {
maven { url 'https://jitpack.io' }
}
}
...
allprojects {
repositories {
maven { url 'https://jitpack.io' }
}
}
- Add gradle dependency
apply plugin: 'kotlin-allopen'
apply plugin:"com.google.devtools.ksp"
.
.
.
implementation 'com.github.SchwarzIT.kokain:kokain-core-api:${latest_version}'
implementation 'com.github.SchwarzIT.kokain:kokain-core-lib:${latest_version}'
//for non android project use 'com.github.SchwarzIT.kokain:kokain-di-jvm' instead
implementation 'com.github.SchwarzIT.kokain:kokain-di:${latest_version}@aar'
// kapt plugin is the old fashion way and might be deleted in future releases use ksp plugin instead
//kapt 'com.github.SchwarzIT.kokain:kokain-processor:${latest_version}'
ksp "com.github.SchwarzIT.kokain:kokain-ksp:${kokain_version}"
.
.
.
allOpen {
annotation("com.schwarz.kokain.api.EBean")
}
- Add EBean annotation to your components
@EBean
open class FooBean : FooBeanInterface {
private val mContext: Context by context()
private val mFooSingletonyBean : FooSingletonBean by inject()
override fun saySomething(): String {
return "something"
}
}
- Add EFactory annotation to your Application and start kokain (GeneratedFactory got automaticaly generated)
@EFactory(additionalFactories = [com.example.demolibrary.GeneratedFactory::class])
class DemoApplication : Application() {
override fun onCreate() {
super.onCreate()
KokainInstance.start(Kokain.create(GeneratedFactory(), this))
}
}
hint: For multimodule projects we need to add the GeneratedFactories for each module via the additionalFactories attribute
- Usage Overview
@EBean
class UsageOverview {
// no difference where the component comes from always use "by inject()"
private val mFooBean: FooBean by inject()
private val mSingletonBean: FooSingletonBean by inject()
private val mClassFromAnotherLibrary : ClassFromAnotherLibrary by inject()
// inject as Interface
private val mFooBeanInterface : FooBeanInterface by inject(FooBean::class)
// inject all sorts of systemservices
private val layoutInflater : LayoutInflater? by systemService()
//inject context (kokain injects activity context if it's save to do so otherwise it injects application context)
private val context : Context by context()
private fun doSomething(){
val bean = get<FooBean>()
bean.saySomething()
}
}
Example based on Mockk
1.) create a simple extension function
/**
* Sets up Kokain to use [mock] whenever an object of its type needs to be injected.
*/
inline fun <reified T : Any> Kokain.setMock(mock: T) {
every {
create<T>(any(), T::class)
} returns mock
}
2.) do some tests
class MySuperFancyControllerTest {
@RelaxedMockK
private lateinit var myBean1: BeanWithCode
@RelaxedMockK
lateinit var kokain: Kokain
@BeforeEach
fun setUp() {
kokain.setMock(myBean1)
KokainInstance.start(kokain)
}
@AfterEach
fun tearDown() {
KokainInstance.stop()
unmockkAll()
}
@Test
fun `do my test magic here without care about kokain`() {
}