Skip to main content
Version: 4.2

Android

This tutorial lets you write an Android application and use Koin dependency injection to retrieve your components. You need around 10 min to do the tutorial.

note

update - 2024-10-21

Get the code

Gradle Setup

Add the Koin Android dependency like below:

dependencies {

// Koin for Android
implementation("io.insert-koin:koin-android:$koin_version")
}

Application Overview

The idea of the application is to manage a list of users, and display it in our MainActivity class with a Presenter or a ViewModel:

Users -> UserRepository -> UserService -> (Presenter or ViewModel) -> MainActivity

The "User" Data

We will manage a collection of Users. Here is the data class:

data class User(val name: String, val email: String)

We create a "Repository" component to manage the list of users (add users or find one by name). Here below, the UserRepository interface and its implementation:

interface UserRepository {
fun findUserOrNull(name: String): User?
fun addUsers(users: List<User>)
}

class UserRepositoryImpl : UserRepository {

private val _users = arrayListOf<User>()

override fun findUserOrNull(name: String): User? {
return _users.firstOrNull { it.name == name }
}

override fun addUsers(users: List<User>) {
_users.addAll(users)
}
}

The UserService Component

Let's write a service component to manage user operations:

interface UserService {
fun getUserOrNull(name: String): User?
fun loadUsers()
fun prepareHelloMessage(user: User?): String
}

class UserServiceImpl(
private val userRepository: UserRepository
) : UserService {

override fun getUserOrNull(name: String): User? = userRepository.findUserOrNull(name)

override fun loadUsers() {
userRepository.addUsers(listOf(
User("Alice", "alice@example.com"),
User("Bob", "bob@example.com"),
User("Charlie", "charlie@example.com")
))
}

override fun prepareHelloMessage(user: User?): String {
return user?.let { "Hello '${user.name}' (${user.email})! 👋" } ?: "❌ User not found"
}
}

The Koin module

Use the module function to declare a Koin module. A Koin module is the place where we define all our components to be injected.

val appModule = module {

}

Let's declare our components. We want singletons of UserRepository and UserService:

val appModule = module {
single<UserRepositoryImpl>() bind UserRepository::class
single<UserServiceImpl>() bind UserService::class
}
info

This tutorial uses the Koin Compiler Plugin DSL (single<T>(), factory<T>()) which provides auto-wiring at compile time. See Compiler Plugin Setup for configuration.

Displaying User with Presenter

Let's write a presenter component to display a user:

class UserPresenter(private val userService: UserService) {

fun sayHello(name: String): String {
val user = userService.getUserOrNull(name)
val message = userService.prepareHelloMessage(user)
return "[UserPresenter] $message"
}
}

UserService is referenced in UserPresenter's constructor

We declare UserPresenter in our Koin module. We declare it as a factory definition, to not keep any instance in memory (avoid any leak with Android lifecycle):

val appModule = module {
single<UserRepositoryImpl>() bind UserRepository::class
single<UserServiceImpl>() bind UserService::class
factory<UserPresenter>()
}

Injecting Dependencies in Android

The UserPresenter component will be created, resolving the UserService instance with it. To get it into our Activity, let's inject it with the by inject() delegate function:

class MainActivity : AppCompatActivity() {

private val presenter: UserPresenter by inject()

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

//...
}
}

That's it, your app is ready.

info

The by inject() function allows us to retrieve Koin instances, in Android components runtime (Activity, fragment, Service...)

Start Koin

We need to start Koin with our Android application. Just call the startKoin() function in the application's main entry point, our MainApplication class:

class MainApplication : Application(){
override fun onCreate() {
super.onCreate()

startKoin{
androidLogger()
androidContext(this@MainApplication)
modules(appModule)
}
}
}
info

The modules() function in startKoin load the given list of modules

Koin module: DSL comparison

Here is the Koin module declaration using Classic DSL (manual wiring):

val appModule = module {
single<UserRepository> { UserRepositoryImpl() }
single<UserService> { UserServiceImpl(get()) }
factory { UserPresenter(get()) }
}

With Compiler Plugin DSL (auto-wiring at compile time):

val appModule = module {
single<UserRepositoryImpl>() bind UserRepository::class
single<UserServiceImpl>() bind UserService::class
factory<UserPresenter>()
}
tip

The Compiler Plugin DSL requires the Koin Compiler Plugin. It provides compile-time dependency resolution and cleaner syntax.