KOIN is a simple (but powerful) dependency injection framework for Android. It uses Kotlin and its functional power to get things done! No proxy/CGLib, no code generation, no introspection. Just functional Kotlin and DSL magic ;)
KOIN is a very small library, that aims to be as simple as possible and let's you write dependency injection in a breath.
Just describe your stuff and inject it!
Check the latest changes in What's New and the Roadmap for next releases.
For users using a version prior to Koin 0.5.x, please refer the migrating to 0.5.0 page to understand the latest changes.
Check that you have the jcenter
repository. Add the following gradle dependency to your Android app:
// Koin for Android
compile 'org.koin:koin-android:0.5.1'
// If you need Koin for your tests
testCompile 'org.koin:koin-test:0.5.1'
To start KOIN and your modules, you just have to use the startAndroidContext
function in your Android Application class like below:
class MainApplication : Application(){
override fun onCreate() {
super.onCreate()
// Start Koin
startAndroidContext(this, weatherAppModules())
}
}
The startAndroidContext
function requires an Application
instance, and a a list of modules to run. A function can manage this for you, check out the weatherAppModules()
function.
KOIN requires you to declare your components in modules.
A module class extends your AndroidModule class and implements the context()
function by using the applicationContext
function builder, to declare a context like below:
class WeatherModule : AndroidModule() {
override fun context() = applicationContext {
// WeatherActivity context
context(name = "WeatherActivity") {
// Provide a factory for presenter WeatherContract.Presenter
provide { WeatherPresenter(get()) }
}
// Weather data repository
provide { WeatherRepository(get()) }
// Local Weather DataSource
provide { LocalDataSource(AndroidReader(applicationContext) } bind WeatherDatasource::class
}
}
//for classes
class WeatherPresenter(val weatherRepository: WeatherRepository)
class WeatherRepository(val weatherDatasource: WeatherDatasource)
class LocalDataSource(val jsonReader: JsonReader) : WeatherDatasource
Your module then provides an applicationContext (description of your components), which will be made of provided components and subcontexts.
You can refer to the KOIN DSL for more details.
Once your app is configured, you have 2 ways of handling injection in your application:
- In Android components (Activity, Fragment etc.): use the
by inject()
lazy operator - In any Kotlin component: injection is made by constructor
// In Android class, use the by inject() operator
class WeatherActivity() : AppCompatActivity() {
// inject my Presenter
val presenter by inject<WeatherPresenter>()
// you can use your injected dependencies anywhere
}
// In pure Kotlin class, All is injected in constructor
class WeatherPresenter(val weatherRepository: WeatherRepository) {
// you can use your dependencies here
}
You can provide a name to a provided component:
class WeatherModule : AndroidModule() {
override fun context() = applicationContext {
provide("MyPresenter") { WeatherPresenter() }
}
}
To get a component with its name, in an Android class:
class WeatherActivity : AppcompatActivity(){
val presenter by inject<WeatherPresenter>("MyPresenter")
}
or in constructor:
class WeatherModule : AndroidModule() {
override fun context() = applicationContext {
provide("MyPresenter") { WeatherPresenter() }
// inject name dependency
provide { WeatherView(get("MyPresenter")) }
}
}
Declare any property from outside of your module :
// Set property key with its value
getKoin().setProperty("key",value)
Resolve it in your module with getProperty("key")
or inject in an Android class with by property("key")
You can also easily bind any Android property:
// bind R.string.server_url to Koin WeatherModule.SERVER_URL
getKoin().bindString(R.string.server_url, WeatherModule.SERVER_URL)
One of the biggest value of Koin, is the ability to drop any instances from a given context, to suits your components life cycle. At any moment, you can use the releaseContext()
function to release all instances from a context.
You can use the ContextAwareActivity
or ContextAwareFragent
to automatically drop an associated context:
// A module with a context
class WeatherModule : AndroidModule() {
override fun context() = applicationContext {
context(name = "WeatherActivity") {
provide { WeatherPresenter(get(), get()) }
}
}
}
class WeatherActivity : ContextAwareActivity(), WeatherContract.View {
// associated context name
override val contextName = "WeatherActivity"
override val presenter by inject<WeatherPresenter>()
//will call releaseContext("WeatherActivity") on onPause() - drop WeatherPresenter instance
}
Up to you to adapt it to your project if need, you are not forced to use those ContextAware components. You can make like follow:
abstract class MyCustomActivity : AppCompatActivity() {
abstract val contextName: String
override fun onPause() {
releaseContext(contextName)
super.onPause()
}
}
KOIN is an internal DSL: all your modules evolves directly with your code (if you change a component, it will also impact your modules).
You can check your modules with KoinContext.dryRun()
(launch all your modules and try to inject each component). Better is to place it in your tests folder and check it regulary - ensure everything is injected correctly.
in a JUnit test file:
@Test
fun dryRun(){
val koinContext = Koin().build(allModules()).dryRun()
// or if you need Application context in your injection
val koinContext = Koin().init(mock(Application::class.java)).build(allModules()).dryRun()
}
You can also use Koin for your tests. You can extend the KoinTest
interface to inject any component from Koin context:
class LocalWeatherPresenterTest : KoinTest {
// Directly injected
val presenter by inject<WeatherContract.Presenter>()
@Mock lateinit var view: WeatherContract.View
@Before
fun before() {
// Mocks
MockitoAnnotations.initMocks(this)
// Koin context
startContext(testLocalDatasource())
presenter.view = view
}
@Test
fun testDisplayWeather() {
Assert.assertNotNull(presenter)
val locationString = "Paris, france"
presenter.getWeather(locationString)
Mockito.verify(view).displayWeather(any(), any())
}
}
The koin-sample-app application offers a complete application sample, with MVP Android style.
The weather app wiki page describes all the KOIN features used.
A global wiki documentation page gather all features and references about the KOIN Framework.
Generated javadoc is available:
- Moving from Dagger to Koin - Simplify your Android development
- Kotlin Weekly #64
- Insert Koin for dependency injection
- Better dependency injection for Android
Check the kotlin slack community and join #koin channel
Don't hesitate to open an issue to discuss about your needs or if you don't a feature for example.