Inverting Our Dependencies

In general, layers of an app should be loosely coupled.

For example, ToDoRepository will be hiding all of the details of exactly where our to-do items get stored. Right now, they are “stored” in memory. Later, they will be stored in a database. They could be stored on a server. And so on. This allows our UI layer to be independent of those storage details.

Our upcoming UI needs access to those to-do items. One approach for this would be to make ToDoRepository be a Kotlin object. That is a global singleton, and our activities and fragments could access it as needed.

On the surface, this is fine. This is a fairly simple app. We are not going to be adding smarts to allow users to “plug in” alternative places for storing the to-do items. One ToDoRepository, in theory, should be enough.

However, even for a small app like this, that argument starts to break down when it comes to testing. We may need to set up specific test implementations of ToDoRepository to test various scenarios, such as what happens when the repository throws an exception (e.g., could not connect to the server). And many apps are much more complicated than this one, where we might really need to have different repository implementations at runtime.

“Dependency inversion” is an approach for dealing with this. In a nutshell, it means that loosely-coupled layers should not be defining the implementations of those other layers. In our app, our activities and fragments should not be declaring that some particular ToDoRepository singleton is the one-and-only repository that those fragments should work from. Rather, our fragments should have their repository objects “injected” from outside, so that in the “real app” we can do one thing and in tests we can do something else.

Part of the problem with dependency inversion in Android is that the historically dominant solution — Dagger — is very complex and has difficult-to-understand documentation. While there have been recent moves to simplify it, such as the Jetpack’s Hilt library, those are very new and are very much “up for debate” at this time.

Kotlin opened up new opportunities for simplifying dependency inversion. One of the more popular Kotlin dependency inversion libraries is Koin. While it may lack some of the power of Dagger, it is good enough for many apps, including the one that we are building here.

You can learn more about dependency inversion with Koin in the "Inverting Your Dependencies" chapter of Elements of Android Jetpack!

So, in this chapter, we will integrate Koin and set it up that ToDoRepository is able to be injected into other objects.

This is a continuation of the work we did in the previous tutorial. The book’s GitLab repository contains the results of the previous tutorial as well as the results of completing the work in this tutorial.

Step #1: Adding the Dependencies

There are a couple of new dependencies that we will need to be able to add Koin to the app. And, similar to the Navigation component, we will have dependencies that need to share a common version number. So, we should define that version number in one place, so when we want to upgrade Koin, we can change the version number in that one place and cover everything.

When we created the nav_version constant, we did so in the buildscript closure of the top-level build.gradle file. That is because the Navigation component includes plugins, so we needed the constant in the buildscript edition of the dependencies. However, Koin does not have a plugin that we will be using, so we should define this constant outside of buildscript, since we do not need it there.

With that in mind, add the following to the bottom of the top-level build.gradle file:

ext {
  koin_version = "3.1.2"
}

This is equivalent to:

ext.koin_version = "3.1.2"

The ext {} syntax is to simplify matters when we need to define more such ext constants, and we will be adding a few more before the tutorials are over.

Then, in the app/build.gradle file, add this line to the dependencies closure:

  implementation "io.insert-koin:koin-android:$koin_version"

Android Studio should be asking you to “Sync Now” in a banner — go ahead and click that link.


Prev Table of Contents Next

This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.