Managing Android Scopes
Taming the Android lifecycle
Android components are mainly managed by their lifecycle: we can't directly instantiate an Activity nor a Fragment. The system make all creation and management for us, and make callbacks on methods: onCreate, onStart...
That's why we can't describe our Activity/Fragment/Service in a Koin module. We need then to inject dependencies into properties and also respect the lifecycle: Components related to the UI parts must be released on soon as we don't need them anymore.
Then we have:
- long live components (Services, Data Repository ...) - used by several screens, never dropped
- medium live components (user sessions ...) - used by several screens, must be dropped after an amount of time
- short live components (views) - used by only one screen & must be dropped at the end of the screen
Long live components can be easily described as single
definitions. For medium and short live components we can have several approaches.
In the case of MVP architecture style, the Presenter
is a short live component to help/support the UI. The presenter must be created each time the screen is showing,
and dropped once the screen is gone.
A new Presenter is created each time
We can describe it in a module:
- as
factory
- to produce a new instance each time theby inject()
orget()
is called
- as
scope
- to produce an instance tied to a scope
note
Most of Android memory leaks come from referencing a UI/Android component from a non Android component. The system keeps a reference on it and can't totally drop it via garbage collection.
Scope for Android Components
Declare a scope
To use a scoped Android component, you have to declare a scope for your component with the scope
block like follow:
Use an Android scope
Koin offers ActivityScope
and FragmentScope
classes, to let you use directly a declared scope for Activity or Fragment:
If you can't use such classes, we need to use the AndroidScopeComponent
interface and implement the scope
property. This will setup the default scope used by your class.
This scope
property can be used with the following property delegates, regarding your component:
activityScope()
- Follow Activity lifecycleactivityRetainedScope()
- Follow Activity's ViewModel lifecyclefragmentScope()
- Follow Fragment lifecycle, and is linked to parent's scopeserviceScope()
- Follow Service lifecycle
note
ActivityScope
class will use a activityScope()
and FragmentScope
class will use fragmentScope()
by default.
We can also setup a scope, tied to a ViewModel with the following:
Sharing instances between components with custom scopes & Scope Links
In a more extended usage, you can use a Scope
instance across components. For example, if we need to share a UserSession
instance.
First declare a scope definition:
When needed to begin use a UserSession
instance, create a scope for it:
Then use it anywhere you need it: