Five Reasons Why You Should Use Koin in Your Next Android Project

Gepostet am: 12. Oktober 2018

For many years now, there was basically only one dependency injection library used in Android app projects: Dagger (2). While Dagger is offering all the features you could ask for and the API has improved a lot with the update from version 1 to 2, it is still quite tedious to set up. Plus—at least for me—it never felt like a very simple and elegant way of satisfying my dependency injection needs. Enter Koin.

With Kotlin gaining more and more popularity (especially among Android developers), a new option for dependency injection has risen: Koin. Koin has existed for a while now, but the first stable version was released quite recently.

Disclaimer: Koin may be used for all kinds of applications, but this article’s focus is on Android app projects (for which Koin offers several bonus features—as described below).

In order to try Koin for myself, I have forked the Kotlin MVP implementation of the famous ToDo App from Google’s Android Architecture Samples and integrated Koin in order to provide the dependencies. The resulting source code can be found here. Note that the author of Koin has done something very similar a couple of months ago, but since then, Koin has changed a bit plus I wanted to try the transition for myself.

Koin can make your life quite a bit easier, especially if you’re already using Kotlin. So, let’s take a look at the five reasons to use Koin:

1. Koin is Easy to Set up

Getting started with Koin could hardly be easier (see Getting Started for Android). All you have to do is:

  1. Add the dependency in your build.gradle:
  2. Create the first module:

    (Note that the Android Context is directly usable as a parameter through Koin’s Android extension)
  3. Start Koin in your Application class (which needs to be set up in the AndroidManifest.xml, of course):
  4. Start injecting:

That’s it. Now you can already use the database in your activity.

Of course, you actually might prefer applying a pattern like MVP or MVVM to keep stuff like database transactions out of your Android activity/fragment/service etc. classes. In case you want to use a Koin-provided dependency in a non-Android class (to be specific, any class not implementing ComponentCallbacks), there’s a small extra step: implement the interface KoinComponent to get access to functions like inject() :

2. Kotlin Features—Elegant DSL and Delegated Properties

As you may have seen above, Koin uses its own DSL instead of annotations and code generation (like Dagger). Here, Kotlin’s language features really shine:

Basically, there are two ways of creating instances— single  creates singletons (like  @Singleton in Dagger) while  factory creates a new instance every time an object of the respective class is injected. In order to match another types, both single  and factory  can use type attributes (there are other ways of dealing with generics as well).

In the example, the TasksPresenter ’s constructor would take one parameter of type ToDoDatabase . Since we have defined a single  for ToDoDatabase  above, this parameter can easily be resolved by using the get()  function.

To me, this elegant way of defining modules is definitely an improvement over code generation—having to hit “build“ before being able to use a newly created module/component can become quite tedious in large projects. Using delegated properties for injection feels like a very natural thing to do in Kotlin. By the way, there are two ways of providing the type: via a type parameter or via an explicit type definition:

One thing to mention here: inject()  is lazy (unless you configure your module or dependency differently)—which is typically what I want when injecting dependencies—while get() is eager.

3. Android Features

As mentioned above, the Android context is directly available in modules via an extension function. In addition to this, Koin provides several features and quite a bit of documentation especially for Android apps:

  • Scopes
    In Android, many dependencies typically need to be available during the lifetime of Android components like activities or fragments. Just creating new instances of dependencies via  factory  may be problematic since this can cause memory leaks (e.g. if a presenter keeps a reference to an Android view/fragment). To avoid that, Koin offers scopes that link a dependency’s availability to the lifecycle of an Android component.
    To use scopes, you need to include a separate module (note that there is another one for AndroidX):

    Defining dependencies using scopes is just as easy as using single  or factory :

    Now both dependencies are available in the scope called “TaskDetailActivity“ – all that is left to do is to bind this scope to the activity’s lifecycle:

    That’s it. Now the dependencies will only exist as long as the activity.
  • Architecture Components and ViewModel
    Koin offers another module for using ViewModels:

    While this sample is using MVP we can also easily inject dependencies in our ViewModels.
    All you need is a ViewModel with a dependency like this

    and this line in your module setup.

    Now you can use your ViewModel in your view like this:

4. Testing

When testing your application, you might want to replace some dependencies with mock instances simulating whatever behavior you need in your tests—this is one of the main advantages of using dependency injection, after all. You might have guessed it—Koin provides a module for testing.

Then, all you have to do is extend KoinTest and start injecting:

Of course, you can exchange modules easily in startKoin() ; but if you want to mock only some classes, Koin has you covered:

The function declareMock() creates a Mockito mock of the respective dependency. In the test above,  the presenter uses a repository and a view, both of which shall be mocked for this case. After this is set up, we can call the function we want to test and verify the expected outcomes. More sophisticated setup (like having the repository return specific tasks) and verification (like capturing and checking arguments) can easily be added using the established Mockito APIs.

In case your dependency tree grows more complex, you might also want to check your modules.

Just like the other Koin features described above, unit testing was very easy to set up and I liked the neat possibility for creating mocks.

5. Logging

Last but not least, Koin logs a lot of information—the modules and dependency declarations as well as information about the creation of dependencies (since they are typically injected lazily) and their transitive dependencies.

This may sound simple, but it can be extremely helpful as the dependency tree grows more complex—especially with multiple build variants replacing modules or (of course!) tests:

Note the lines about mock declarations.

For everyone who has at some point struggled to understand the relationship between injected dependencies (which is probably just about anyone who has used dependency injection in a larger project), this should help a lot. Of course, Koin includes many features I didn’t mention above (the feature set is quite impressive for a 1.0), but as a new user, those were the parts I enjoyed the most when working with Koin.

Conclusion

Most of all, Koin is easy to use and integrates well in a Kotlin Android project (btw, it may be used for Java projects as well). With the 1.0.0 only released a short while ago, the project feels very mature and stable; also the documentation is pretty extensive.

Compared to Dagger 2, using Koin felt a lot easier, more fun and so far I haven’t encountered any missing features. So go ahead and try it!

2018-10-12T09:59:31+00:00