Understanding Koin Basics

Megha Vaishy
5 min readMay 30, 2021

Dependency injection is a technique where objects are created by an external entity or object. Dagger2 , Hilt , Koin are commonly used Dependency injection framework available in android. As I moved to work on Koin based enterprise-scale android project from dagger2 framework just thought to share quick basic understanding of it.

1. Koin uses Kotlin’s DSLs to lazily resolve your dependency graph at runtime. It doesn’t do anything at compile time. It’s a much smaller and more lightweight library than Dagger, as doesn’t generate any code.

Koin DSL keywords

module { } — create a Koin module or a submodule (inside. a module)

factory { } — provide a factory bean definition

single { } — provide a bean definition

get() — resolve a component dependency (eager)

inject() — resolve a component dependency (lazy)

2. Module:

In Koin a module is a component in which we declare all the dependencies. that will be injected in another components(ViewModel, Fragments, Activity).

Sample of module in koin :val dataStoreModule = module {
factory { CompositeDisposable() }
single { SharedPreferenceManager(androidContext()) }

single {
DataStore(
get<SharedPreferenceManager>(),
get<CompositeDisposable>()
)
}

single { AssetManager(androidContext()) }

single { RemoteConfigManager(get<DataStore>()) }
}

Here dependency injection is achieved with the container (your modules) that help to inject your instances by constructor. The module definition is very similar to Dagger-2 @Module definitions, it serves as a container for a collection of services that should be provided at runtime.

3. Injection Design Pattern:

It is based on Service Locator Pattern whereas dagger is based on dependency injector Pattern . In this pattern dependant(Activity or Fragment or ViewModel) access its dependencies from. a service locator (container), that helps to search for the dependencies and provide to it.

While in Dependency Injection dependencies are injected into dependant at compile time.

4. Error Handling :

As Dagger is a compile-time dependency injection framework if we forgot to provide some dependency we will know about our mistake almost instantly because project will fail to build.

For example, if we forgot to add @Inject annotation to the constructor and try to inject it in a fragment, the build will fail with an appropriate error which shows us exactly what went wrong. As shown below it builds output where there is @ Inject annotation missing

class AudioDevicePresenter @inject constructor(){
}

In Koin the situation is different. Because it does not generate any code if we forgot to add a factory for AudioDevicePresenter class, an app will build, but it will crash with RuntimeException once we request an instance of this class. It might happen at app start so we might notice it right away, but it can also happen later, on some further screen or when the user performs some specific action.

5. Viewmodel Injection

Without Koin viewmodel instance is created in activity/fragment like below

viewModel=ViewModelProviders.of(this,viewModelFactory).get(DetailViewModel::class.java)

Here ViewModelProviders uses injecting viewModelFactory to get instance of viewModel

In Koin viewmodel is super easy to declare, as it fully supports viewmodels it takes care of everything for us so no need to manually create viewmodelfactory to provide dependencies to our viewmodels

koin-android-viewmodel library has a new viewModel DSL keyword that help declare a ViewModel component and bind it to an Android Component lifecycle.

class KoinViewModel(private val koinRepo: KoinRepo) : ViewModel() {}val viewModelModule = module {viewModel { KoinViewModel(get<KoinRepo>() }}class ArticleListFragment : Fragment() {

// Using Koin-ViewModel library, we can have it create our ViewModels and remove all of the factory boilerplate
val viewModel: KoinViewModel by viewModel()
}

here get() function automatically picks and provides the required dependency(we have to define it in our modules function) to our ViewModel’s constructor.

6. Dependency Management:

Dagger 2 walks through the dependency graph and generates code automatically during build time.The overridden get() method inside autogenerated factory class simply creates a new instance and use given provider to resolve dependencies of that instance.The generated code includes factories for all your dependency graph classes to wire up together on build.

In Koin it internally uses power of Reified to link up all its dependencies. Reified in kotlin is used to access the information about the type of class at runtime.The main API call of Koin i.e. inject, get below uses reified

// eager dependency approachval koinRepo: KoinRepo = get()// Koin will lazily inject this dependency, when we need itval koinRepo: KoinRepo by inject()
@JvmOverloads
inline fun <reified T> inject(
qualifier: Qualifier? = null,
noinline parameters: ParametersDefinition? = null
): Lazy<T> =
lazy(LazyThreadSafetyMode.NONE) { get<T>(qualifier, parameters) }
@JvmOverloads
inline fun <reified T> get(
qualifier: Qualifier? = null,
noinline parameters: ParametersDefinition? = null
): T {
return get(T::class, qualifier, parameters)
}

InstanceRegistry class of Koin’s framework stores all dependencies info into a map table. When I launched my application i could see there are125 references in InstanceRegistry class map

when we call inject / get() inside Activity or fragment Koin will provide required bean dependency from map of InstancesRegistry class at runtime. The _instances map inside registry class needs key to populate required bean reference and inject it in respective activity/ fragment. Here key is nothing but full name of a class. This is how Koin works to fetch bean reference internally and do dependency management.

Summary

Koin is easier to learn and adopt and has simpler setup configuration if compared to other DI frameworks as i learned and understood within a day.

--

--