Koin vs Dagger, Say hello to Koin
After many times trying to learn Dagger finally I found Koin. Koin saves my time, my life and my brain.
This article explains what Koin is, compare with Dagger and how to use it.
What is Koin
Koin is a simple powerful Dependency injection framework for Kotlin. Written in pure Kotlin using functional resolution only: no proxy, no code generation, no reflection!
Compare to Dagger
In order to properly compare the two implementation options, we will focus on a project that Implemented first with Dagger and now with Koin. The architecture of this project is MVVM and it uses retrofit and LiveData. My project has an Activity, four fragments, five view models, a repository and a web service interface, so we can say it’s a small project just for designing a base for my future applications.
First look the DI package before and after Koin
As you see, Dagger configuration needs pretty much more files to write. for example three files to injecting view models!! ( Really ? is it necessary? )
Lines of code:
To get these numbers I used Statistic. I used it twice before and after compiling the project with both Dagger and Koin. The result was amazing.
As you can see Dagger generated line of code two times more than Koin!
How about Build time:
I tried to clean and rebuild both project. I got this result:
Koin:
BUILD SUCCESSFUL in 17s
88 actionable tasks: 83 executed, 5 up-to-dateDagger:
BUILD SUCCESSFUL in 18s
88 actionable tasks: 83 executed, 5 up-to-date
I think this result shows us that in a bigger and real project this time could be expensive.
Setup
Another thing is Setting up these libraries. think you want to use Dagger with MVVM and Android Support lib so you have to do this:
First adding Dagger SDK to module gradle:
kapt "com.google.dagger:dagger-compiler:$dagger_version"
kapt "com.google.dagger:dagger-android-processor:$dagger_version"
implementation "com.google.dagger:dagger:$dagger_version"
After creating modules and components you have to init Dagger in Application class (or other ways to initializing Dagger)
Class MyApplication : Application(), HasActivityInjector {
@Inject
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Activity>override fun activityInjector() = dispatchingAndroidInjectorfun initDagger() {
DaggerAppComponent
.builder()
.application(this)
.build()
.inject(this)
}
}
Like this, in all activities or in our BaseActivity, we need to implement HasSupportFragmentInjector and inject DispatchingAndroidInjector.
For view models, we need to inject ViewModelFactory in BaseFragment and also implement Injectable.
But it not about all. There are more to do.
For each ViewModel, Fragment, and Activity we need to tell DI to how to inject them. As you can see we have ActivityModule, FragmentModule, and ViewModelModule.
Let me show you some codes:
@Module
abstract class ActivityModule {
@ContributesAndroidInjector(modules = [FragmentModule::class])
abstract fun contributeMainActivity(): MainActivity
//Add your other activities here
}
For Fragments:
@Module
abstract class FragmentModule {
@ContributesAndroidInjector
abstract fun contributeLoginFragment(): LoginFragment
@ContributesAndroidInjector
abstract fun contributeRegisterFragment(): RegisterFragment
@ContributesAndroidInjector
abstract fun contributeStartPageFragment(): StartPageFragment
}
And for ViewModels:
@Module
abstract class ViewModelModule {
@Binds
abstract fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory
@Binds
@IntoMap
@ViewModelKey(loginViewModel::class)
abstract fun bindLoginFragmentViewModel(loginViewModel: loginViewModel): ViewModel
@Binds
@IntoMap
@ViewModelKey(StartPageViewModel::class)
abstract fun bindStartPageViewModel(startPageViewModel: StartPageViewModel): ViewModel ......}
So you have to care about adding new Fragments, Activities, and ViewModels to DI modules, also, you have to say all developers to care about that too.
What about Koin:
Just add this line to your gradle:
implementation "org.koin:koin-android-viewmodel:$koin_version"
And create your module file, I’ll talk about it later but I could say we don’t need any of these classes.
There is some other problem with Dagger.
Learning Dagger is hard, so if someone joins your project or team he/she has spent a lot of time on learning Dagger. I used to Dagger since two years ago and now I don’t know everything about that, every time I started to use new tech in android I have to search and learn about Dagger to implement this new tech with Dagger.
Time To Code
Ok, after all, let’s coding.
As you know we have to add Koin Dependency first, so let’s do it:
implementation "org.koin:koin-android-viewmodel:$koin_version"
This version of Koin is used for Koin with ViewModel, there are some other dependencies to use Koin, But we want to use it with MVVM
After adding Koin dependency we can implement our first module, just like Dagger we can implement every module in a separate file, but because of code simplicity, I decided to implement all modules in a file. you can separate it later.
First, we have to know some useful notes about koin syntax:
get()
to resolve an instance in a Koin module, just use theget()
function to the requested needed component instance. thisget()
function is usually used into the constructor, to inject constructor values.factory
factory component declaration is a definition that will gives you a new instance each time you ask for this definitionsingle
provide a singleton bean definition (also aliased asbean
)name =
is used to name definitions. This is required when you want to have multiple instances of the same class with different types.
Why my codes are removed from Here?!!
See the GitHub for more detail
That's it. Instead of creating many files with multiple annotations, multiple components, we create a simple and readable for every one class for DI!!
Explain the code
private val retrofit: Retrofit = createNetworkClient()
createNetworkClient is a function to create a Retrofit instance. setting baseUrl, adding ConverterFactory and Interceptor done there.
private val generalApi: GeneralApi = retrofit.create(GeneralApi::class.java)private val authApi: AuthApi = retrofit.create(AuthApi::class.java)
AuthApi and GeneralApi are retrofit endpoints interfaces. we just add our endpoint here to separate our endpoint.
val viewModelModule = module {
viewModel { LoginFragmentViewModel(get()) }
viewModel { StartPageViewModel() }
}
we declare our viewModels class as a viewModel
in a module
. Koin will give a viewModel
to the lifecycle ViewModelFactory and help bind it to the current component.
as you can see we have a get() method into the LoginFragmentViewModel constructor. this function resolves an instance for LoginFragmentViewModel. here it’s AuthRepo.
After all, we have to say DI to generate codes, but How?
It’s a piece of cake
in your Application class in the onCreate Method write this:
startKoin(this, listOf(repositoryModule, networkModule, viewModelModule))
Here we simply call the startKoin method, passing in a context and a list of the modules which we wish to initialize Koin with.
Using ViewModel is easier than pure ViewModel now!
in your View class (Fragment, Activity) add this:
private val startPageViewModel: StartPageViewModel by viewModel()
By this code, koin creates an object of StartPageViewModel for you and fill the startPageViewModel with it. Now you can use your view model in your view.
Find my project on GitHub.
Hope that it makes you want to try Koin
Thanks to Mario Sanoguera de Lorenzo
📝 Read this story later in Journal.
👩💻 Wake up every Sunday morning to the week’s most noteworthy stories in Tech waiting in your inbox. Read the Noteworthy in Tech newsletter.