Injection
Most of your classes receive their dependencies through their constructor, and Koin fills them (see Constructor parameters). Some code can't: an Android Activity, a main() function, a class created by a framework. This page covers the functions that retrieve instances from Koin in that code: get(), inject(), their nullable variants, and getAll().
Constructor injection first
Use the functions on this page only where Koin can't create the class:
// Koin creates this class: dependencies come through the constructor
class UserService(
private val repository: UserRepository,
private val validator: UserValidator,
)
// Android creates this class: retrieve dependencies with inject()
class UserActivity : AppCompatActivity() {
private val userService: UserService by inject()
}
A class with constructor parameters can be tested by passing fakes, with no Koin container. A class that calls get() needs a running container in its tests. Never call get() in a default parameter value (class UserService(val repository: UserRepository = get())): declare the class in a module instead.
Lazy and eager retrieval
Retrieval code is the same in every style:
class UserActivity : AppCompatActivity() {
// lazy: resolved on first access of the property
private val userService: UserService by inject()
// eager: resolved now, when the property is initialized
private val analytics: Analytics = get()
}
| Function | Returns | Resolved |
|---|---|---|
by inject<T>() | Lazy<T> used as a property delegate | On first access |
get<T>() | T | Immediately |
Use by inject() for properties. The class can be created before Koin has everything ready (an Activity field is initialized before onCreate()), and an instance that is never used is never created. Use get() inside a function body, when you need the value now.
inject() takes a mode parameter, a Kotlin LazyThreadSafetyMode. The default is SYNCHRONIZED on the JVM.
Where get() and inject() are available
You can call get() and by inject() from:
- Android classes:
Activity,Fragment,Serviceand otherComponentCallbacks, with no extra interface. See Android Entry Points. - Any class that implements
KoinComponent. See KoinComponent. - A
Koininstance:KoinPlatform.getKoin().get<UserService>()uses the global container,koinApp.koin.get<UserService>()an isolated one. - Inside a definition (Classic DSL):
get(),inject(),getOrNull(),injectOrNull()andgetAll()resolve from the current scope.
Platform APIs follow the same rules with their own names:
- Compose:
koinInject()andkoinViewModel(). See Koin for Compose. - Android ViewModel:
by viewModel()andby activityViewModel(). See Android ViewModel. - Ktor:
by inject()inApplication,RouteandApplicationCall. See Koin for Ktor.
Optional dependencies
When a definition may be missing, use the nullable variants. They return null instead of throwing NoDefinitionFoundException:
val koin = KoinPlatform.getKoin()
val tracker: Tracker? = koin.getOrNull()
val crashReporter: CrashReporter? by koin.injectOrNull()
getOrNull() and injectOrNull() exist on Koin, on Scope and inside definitions. The KoinComponent interface provides get() and inject() only: call getKoin().getOrNull<T>() there.
For a constructor parameter, declare it nullable (tracker: Tracker?) and Koin passes null when no definition exists. See Constructor parameters.
If Koin itself may not be started, for example in a library, KoinPlatform.getKoinOrNull() returns null instead of throwing.
All instances of a type
getAll<T>() returns every instance bound to the type T, across all definitions:
val koin = KoinPlatform.getKoin()
val interceptors: List<Interceptor> = koin.getAll()
The list contains each definition whose main type or bound type is T, with or without qualifier. It is empty when there is none. For a constructor parameter, a List<Interceptor> parameter does the same. Collection patterns are covered in Advanced Patterns.
Qualifiers and parameters at the call site
Every retrieval function takes a qualifier and a parameter block:
val localDb: Database by inject(named("local"))
val presenter: UserPresenter = get { parametersOf(userId) }
val detail: DetailPresenter = get(named("compact")) { parametersOf(itemId) }
- Qualifier: picks one definition among several of the same type. See Qualifiers.
- Parameters: values only the caller knows, passed to the definition. See Injected Parameters.
With by inject(), the parameter block runs on first access, so it can read properties that are set after construction.
When resolution fails
get() throws NoDefinitionFoundException when no definition matches the type and qualifier. With by inject(), the exception comes on first access of the property, not where you declared it. With the Compiler Plugin, get() and inject() calls are also checked at build time against the graph of your start call.
For common errors and how to read Koin's messages, see Troubleshooting.
Related
- Definitions: constructor injection and how parameters are resolved.
- KoinComponent: give any class access to
get()andinject(). - Android Entry Points: inject in Activity, Fragment, Service, Receiver and View.
- Koin for Compose:
koinInject()and other Compose APIs. - Scopes: retrieve instances from a scope instead of the root container.