Koin Library in Android

Oğuzhan Aslan
7 min readMar 2, 2021

There are lots of questions out there, asking that what is Koin? How do we use it Android ? and Why should we use it in Android ?. In this article , I will try to answer some of these questions and try to explain Koin itself .

First of all , Lets answer the question “what is Koin?”. Koin is a Dependency Injection library(DI). As It stated in the offical Koin website:

Koin — a smart Kotlin injection library to keep you focused on your app, not on your tools.

With using Koin , we can inject the depencencies we need in any class such as Activities,Servies or ViewModels. Therefore, our code becomes more reusable, more easy to test and refactor.

Secondly, when it comes to the “why” part, the question generally is “ why should I use Koin, If we already have Dagger2 or Hilt ?“.In fact, there is no such a thing that “you should use”.Both of them have advantages and disadvantages (Hilt is built on top of the Dagger2 ,so I count them as one). One advantage that Koin has over the Dagger2/Hilt is the fact that There is no code generation at all,so the compile time is shorter.Additionally, The errors in Dagger2/Hilt are much harder to trace and debug (due to the code generation).On the other hand, Koin also have disadventages as well. For instance, although solving errors in Dagger2/Hilt are harder than the Koin errors. We cannot detect Koin errors in compile time which means we will face with them in the runtime.The Libraries have their own advantages that are not stated here. You can check out this blog to get further information.

Moreover, the “how” question is easier than the others to answer since Koin library is so simple to learn , especially if you work with other DI libraries such as Dagger2/Hilt.

Before we start , we should learn some Koin syntax .

· Module :

modules are defined as.

Module Declaration in Koin

This syntax can be a bit confusing at first, especially for those who are new to Kotlin but in essence, this is just a function that uses SAM feature of Kotlin. If we look at the source code, this is just a well defined function

Module Fuction Declaration

Understanding this is important due to the fact that other functions in Koin are mostly using this logic.

The Equvailent of this modulefunction in Hilt/Dagger is

@Module
@InstallIn(...)
object myModule {
...
}

· Single:

the singletons in koin are defined as

single {
MyClass()

}

with this declaration now we can use the same instance of MyClass anywhere we inject it.What’s more, this function needs to be placed inside of a module in order to be used.As an example , we can declare the singleton above as

val myModule = module {
single {
MyClass()

}
}

· Factory:

we may not need same instance of a class, we may need new instance of that class. This is why the factory function is used for. We can can define a factory as follows

factory{
MyOtherClass()

}

Now, whenever we try to inject the MyOtherClass , Koin Library will create a new instance of the class and inject it.

This function also needs to be placed in a module , just like single functions.

Note:

It is not a necessity to use our own classes in single or factory , we are free to use the built-in classes as well.

· Injection:

For injecting our depencencies into the classes, we have 2 choices. we may either use get() or inject()

get() : get() is just a function that we can call it to get the dependency we need, Koin is smart enough to determine which dependency should be injected. An example usage of this function is as follows

val myClass = get<MyClass>()
val myOtherClass : MyOtherClass = get()

As you can see above, we need to declare the type we want to inject if the value/variable does not have a specific type.If it does, on the other hand, we can simply call get() function and Koin does the rest.

inject() : This function is a delegate for initilaizing dependencies lazily.To give an example,

val presenter : Presenter by inject()

with this way, the depencency will be injected when we use it for the first time.The best thing this delegate , for me , is that we do not need to specify the properties as public .In contrast, we are able to declare them as private (this option does not exsits in Dagger/hilt). Because of this , we may declare the presenter above like this :

private val presenter : Presenter by inject()

Note : I need to mention that if you need to pass a dependency to any class in your module you can pass that by using get() function.

val myModule = module {
single {
MyClass(
get()
)

}
single {
MyDependency()

}
}
class MyClass(dependency: MyDependency)

Using in Android

Before we start, Make sure that you declare the dependencies in the build.gradle(:app) file

// Koin for Android
implementation "org.koin:koin-android:$koin_version"
// Koin Android Scope features
implementation "org.koin:koin-android-scope:$koin_version"
// Koin Android ViewModel features
implementation "org.koin:koin-android-viewmodel:$koin_version"

So far, we have basic knowledge about Koin Library and its syntax, functions. Let’s get into its usage in Android.

To begin with, the need to “start” Koin in our Application class.

class MainApplication: Application() {
override fun onCreate() {
super.onCreate()
startKoin {
androidContext(this@MainApplication)
modules(MyModule.module)
}
}
}
object MyModule{

val module = module {
...
}

}

with this way, Koin knows which modules that will be used. The modules() function takes module(s) as vararg which means we can pass different number of modules such as

modules(MyModule.module1,MyModule.module2,MyModule.module3)

We can also pass a list of modules as well

val myModules = listof(MyModule.module1,MyModule.module2,MyModule.module3)
modules(myModules)

In addition , androidContext(this@MainApplication) line , basically, gives our application Context to the Koin so that it will be able to pass the context dependency whenever we need. To get the Context inside of a module we can simply call it with using androidContext() . As an example,

Example usage of androidContext() (from offical docs)

I hope everything looks clear so far. There is one more important point for Koin we should know about and that is ViewModel injection. As you may know ViewModels have different lifecycle that other components in the android (you can check here for further information)therefore there is a different way to inject it.

In order to create a ViewModel inside of a module, we use viewModel() function and pass a lamda to it (as you might guess).

val appModule = module {
viewModel { myViewModel() }
}

we can also pass its dependency over here. Even if its dependencies are put in another module, Koin is smart enough to find and pass them to our viewModel .

val appModule = module {
viewModel { MyViewModel( get(), get() ) }
}
val anotherModule = module {
single { ViewModelDependency_1() }
single { ViewModelDependency_2() }
}

I hope everything is clear so far. The only part is remaining is injecting our dependencies where they are belong.

For the dependencies except ViewModel , We simply use get() or inject() . Lets assume we have a NetworkService and a LocalsService in our module

val appModule = module {
single{ NetworkService()}
single{ LocalsService()}
}

For the classes that needs these dependencies. We can pass them directly.In other words, using field injection.

class MainActivity: AppCompatActivity() {     val networkService: NetworkService by inject()     override fun onCreate(savedInstanceState: Bundle?) {           super.onCreate(savedInstanceState)
val localService : LocalService = get()
}
}

Or We may choose to injecting them by constructor injection.However, we cannot use an Activity for that, I must add.

class MyRepository(
networkService:NetworkService,
localsService:LocalsService
)
val appModule = module {
single{ NetworkService()}
single{ LocalsService()}
single{ MyRepository( get(), get() )
}

It is not necessity to put single{ MyRepository(get(),get()) in the same module with other single definitions, I are free to put it inside of any module we declare as long as we give it to the Koin (in the Application class, remember).

For the ViewModel class injecting is done in a similar way but using different functions. We can inject a ViewModel by using either viewModel() delegate or by getViewModel() function.

class MainActivity: AppCompatActivity() {     val networkService: NetworkService by inject()     val myViewModel: MyViewModel by viewModel()     override fun onCreate(savedInstanceState: Bundle?) {           super.onCreate(savedInstanceState)
val localService : LocalService = get()
val myOtherViewModel: MyViewModel = getViewModel() }
}

All in all , we have learned about koin library and its basic usage in android.Of course, the library has more than we talked in this article.If you want to learn more about it,you can read their official documentary. In order to follow my other articles do not forget to stay tune.Take Care.

--

--