Understanding Android Scopes with Koin
By: Arnaud Giuliani
Tags: Android, Android App Development, AndroidDev, Koin, Dependency Injection

This article is inspired by Manuel Vivo’s article, Scoping in Android and Hilt. Manuel has shown how it works for Dagger Hilt. I will explain here how it works for Koin.

What is a scope?

A scope/is a fixed duration of time or method calls in which an object exists. In other words, a scope is a context under which a given key refers to the same instance. Another way to look at this is to think of scope as the amount of time an object’s state persists. When the scope context ends, any objects bound under that scope are said to be /out of scope/and cannot be injected again in other instances.

— Dependency Injection by Dhanji R. Prasanna

In a nutshell, a scope is a space where we are maintaining instances for a specific duration of time. What about Android scope? We maintain instances for the duration of an Android component lifecycle.

Android components have their own lifecycle (Activity, Fragment, Service …). Each property inside those instances follows the underlying component’s lifecycle. In other words, your properties will be garbage collected at the end of your Android Component lifecycle.

Let’s take a simple example:

The instance of MyPresenter will follow MyActivity instance lifecycle. Once the Activity will finish, the presenter instance will be removed from memory. On new MyActivity instance, the presenter property will receive a new instance.

On configuration changes, both instances are recreated

Let’s declare it with Koin 😃

️Do you need a scope?

If our properties can be dropped at the end of the lifecycle, why do we need to care about scopes? 🤔

We need a scope for the following reasons:

keep the same instance of a component, for all other components of the scope

keep the same instance alive, for a specific period of time

Here are some regular use-cases of scopes, applied to Android applications:

Android Lifecycle scope — instance tied to an Android lifecycle component (Activity, Fragment …)

— instance tied to an Android lifecycle component (Activity, Fragment …) Custom scope — instance kept for a specific duration of time (more than one Activity, i.e: wallet …)

Let’s declare our scope for MyActivity with the scope Koin DSL keyword. By writing scope<MyActivity> , we open a code block to define our scoped instances. Each scoped component will be declared with the scoped keyword.

Defining MyActivity scope, with scoped definitions MyAdapter and MyPresenter

This way we can share our MyPresenter instance with another definition of the scope. We use the get() keyword to resolve the MyPresenter instance for the MyAdapter instance.

Now, in our MyActivity class let’s use the AndroidScopeComponent interface and implement the needed scope property with activityScope() delegate function to activate our scope as follows:

We use the AndroidScopeComponent interface and setup our scope with activityScope() function

📌 By using the AndroidScopeComponent interface, you need to override a scope property. This scope will be automatically used in all default API without doing anything else (inject, get, viewModel…) 📌 the activityScope() delegate function ensures removal of your scoped instances at the end of your Activity lifecycle

Note that Koin also proposes the ScopeActivity class, to help wrap the Activity scope declaration. Check the documentation for more details.

However, on configuration changes, we still lose our MyPresenter instance. On a new MyActivity instance, we are creating a new Activity scope.

On configuration changes, both instances are recreated

ViewModel: a game-changer

Since 2017, the Android architecture components are clearly helping to deal with this kind of lifecycle problem by proposing the ViewModel class. This new component will follow your Activity/Fragment instance and survive a configuration change. It simplifies the lifecycle management with only one lifecycle method to implement: onCleared()

A ViewModel is super easy to declare in Koin 😁

The ViewModel instance is handled by an internal Android factory, allowing it to survive configuration changes.

On configuration changes, the MyViewModel instance is kept

Similar Dagger Hilt, there is a special scope to survive configuration changes.

The activityRetainedScope() function will hold instances via a ViewModel holder instance for you.

We can keep our MyPresenter instance, through configuration changes

Every scope has an End

Taking control of scoped instances makes you responsible for closing them. The Koin Scope API helps automatically handle Android scopes for you:

the AndroidScopeComponent interface will help setup a scope for your Android component, by using activityScope() or activityRetainedScope() delegates for your scope property

interface will help setup a scope for your Android component, by using or delegates for your property ScopeActivity class helps wrap everything for you

The same API exists for Fragment and Service classes. The Fragment scope automatically binds your parent Activity scope. Check the Android Scope documentation for more details: https://insert-koin.io/docs/reference/koin-android/scope

A custom scope helps you maintain an app state for a given duration. Take care to properly setup your create/close sequence. You can check the following documentation for more information about Scope API: https://insert-koin.io/docs/reference/koin-core/scopes

Be sure to use only scoped instances if you really need them. Keep things as simple as possible 👍

From Manuel Vivo’s article: