Android Scopes
This guide covers Android-specific scope implementations.
For core scope concepts, see Scopes.
Overview
Scopes in Koin allow you to manage the lifecycle of your dependencies to match Android component lifecycles. This prevents memory leaks and ensures proper resource management.
Scope Hierarchy
| Scope Type | Lifetime | Survives Rotation | DSL | Annotation |
|---|---|---|---|---|
| Application | Entire app | ✅ Yes | single { } | @Singleton |
| Activity | Activity lifecycle | ❌ No | activityScope { } | @ActivityScope |
| Activity Retained | Until finish() | ✅ Yes | activityRetainedScope { } | @ActivityRetainedScope |
| Fragment | Fragment lifecycle | ❌ No | fragmentScope { } | @FragmentScope |
| ViewModel | ViewModel lifecycle | ✅ Yes | viewModelScope { } | @ViewModelScope |
Scope Relationships
Application Scope (single { })
└── Activity Retained Scope (survives rotation)
└── Activity Scope
├── Fragment Scope 1
└── Fragment Scope 2
└── ViewModel Scope (can't access Activity/Fragment scope)
Key Principle: Child scopes can access parent scope definitions, but not vice versa.
Declaring Scoped Dependencies
Compiler Plugin DSL
val appModule = module {
// Activity scope
activityScope {
scoped<ActivityPresenter>()
scoped<ActivityNavigator>()
}
// Fragment scope
fragmentScope {
scoped<FragmentPresenter>()
}
// ViewModel scope
viewModelScope {
scoped<UserCache>()
viewModel<UserViewModel>()
}
}
Annotations
// Activity scope
@ActivityScope
class ActivityPresenter(private val repository: UserRepository)
@ActivityScope
class ActivityNavigator
// Activity retained scope (survives rotation)
@ActivityRetainedScope
class RetainedPresenter
// Fragment scope
@FragmentScope
class FragmentPresenter
// ViewModel scope
@ViewModelScope
class UserCache
@KoinViewModel
@ViewModelScope
class UserViewModel(private val cache: UserCache) : ViewModel()
Classic DSL
val appModule = module {
activityScope {
scoped { ActivityPresenter(get()) }
scoped { ActivityNavigator() }
}
fragmentScope {
scoped { FragmentPresenter(get()) }
}
viewModelScope {
scoped { UserCache() }
viewModel { UserViewModel(get()) }
}
}
Using Scopes in Android Components
Activity Scope
class MyActivity : AppCompatActivity(), AndroidScopeComponent {
// Create scope tied to Activity lifecycle
override val scope: Scope by activityScope()
// Inject from scope
private val presenter: ActivityPresenter by inject()
}
Or use the convenience base class:
class MyActivity : ScopeActivity() {
// Scope is already set up
private val presenter: ActivityPresenter by inject()
}
Activity Retained Scope
Survives configuration changes (rotation, theme change):
class MyActivity : AppCompatActivity(), AndroidScopeComponent {
// Backed by ViewModel lifecycle - survives rotation
override val scope: Scope by activityRetainedScope()
private val presenter: RetainedPresenter by inject()
}
Or use the convenience base class:
class MyActivity : RetainedScopeActivity() {
private val presenter: RetainedPresenter by inject()
}
Fragment Scope
Fragment scopes are automatically linked to parent Activity scope:
class MyFragment : Fragment(), AndroidScopeComponent {
override val scope: Scope by fragmentScope()
// From fragment scope
private val presenter: FragmentPresenter by inject()
// Can also access Activity scope dependencies
private val activityPresenter: ActivityPresenter by inject()
}
Or use the convenience base class:
class MyFragment : ScopeFragment() {
private val presenter: FragmentPresenter by inject()
}
Type-Based vs Archetype Scopes
Archetype Scope (Recommended)
Generic scope that works with any Activity/Fragment:
module {
activityScope {
scoped<MyPresenter>()
}
}
// Works with any Activity
class ActivityA : ScopeActivity() {
private val presenter: MyPresenter by inject()
}
class ActivityB : ScopeActivity() {
private val presenter: MyPresenter by inject()
}
Type-Based Scope
Scope tied to a specific class:
module {
scope<MyActivity> {
scoped<MyPresenter>()
}
}
// Only works with MyActivity
class MyActivity : AppCompatActivity(), AndroidScopeComponent {
override val scope: Scope by activityScope()
private val presenter: MyPresenter by inject()
}
ViewModel Scope
ViewModels cannot access Activity or Fragment scopes (to prevent memory leaks). Use ViewModel Scope for scoped dependencies:
module {
viewModelScope {
scoped<UserCache>()
scoped<UserRepository>()
viewModel<UserViewModel>()
}
}
@ViewModelScope
class UserCache
@ViewModelScope
class UserRepository(private val cache: UserCache)
@KoinViewModel
@ViewModelScope
class UserViewModel(
private val repository: UserRepository
) : ViewModel()
For detailed ViewModel scope usage, see Scopes - ViewModel Scope.
Scope Lifecycle
Handling Scope Close
Override onCloseScope() to run cleanup before scope is destroyed:
class MyActivity : AppCompatActivity(), AndroidScopeComponent {
override val scope: Scope by activityScope()
override fun onCloseScope() {
// Called BEFORE scope.close()
// Scope is still accessible here
}
}
Don't access scope in onDestroy() - it's already closed at that point.
Scope Links
Share instances between components with custom scopes:
module {
scope(named("session")) {
scoped<UserSession>()
}
}
class MyActivity : ScopeActivity() {
fun startSession() {
val sessionScope = getKoin().createScope("session", named("session"))
// Link to current scope
scope.linkTo(sessionScope)
// Now UserSession is accessible
val session: UserSession = get()
}
}
Quick Reference
| Component | Delegate | Base Class |
|---|---|---|
| Activity | by activityScope() | ScopeActivity |
| Activity (retained) | by activityRetainedScope() | RetainedScopeActivity |
| Fragment | by fragmentScope() | ScopeFragment |
| Scope | Survives Rotation | Use Case |
|---|---|---|
activityScope | ❌ No | UI state, presenters |
activityRetainedScope | ✅ Yes | Form state, pending requests |
fragmentScope | ❌ No | Fragment-specific presenters |
viewModelScope | ✅ Yes | ViewModel dependencies |
Best Practices
- Use archetypes - Prefer
activityScope { }overscope<MyActivity> { }for reusability - Retained for rotation - Use
activityRetainedScopefor state that should survive rotation - Don't leak - Never inject Activity/Fragment into singletons
- Close custom scopes - Always close manually created scopes
- Use onCloseScope - For cleanup before scope destruction
Next Steps
- Core Scopes - Scope fundamentals and ViewModel scope
- ViewModel - ViewModel injection
- Testing - Testing scoped dependencies