Skip to main content
Version: 4.2

Scopes

Scopes control the lifecycle of your dependencies. This guide covers how to define, create, and manage scopes.

Understanding Scopes

Scope TypeLifecycleExample
Single (Singleton)App lifetimeDatabase, ApiClient
FactoryPer requestPresenters, Use Cases
ScopedPer scopeActivity-bound, Session-bound

When to Use Scopes

Use scopes when you need:

  • Instances that live longer than a factory but shorter than a singleton
  • Shared state within a specific context (Activity, Fragment, Session)
  • Automatic cleanup when a context ends

Defining Scoped Definitions

DSL

val appModule = module {
// Scope for MyActivity
scope<MyActivity> {
scoped<Presenter>()
scoped<Navigator>()
}

// Named scope
scope(named("session")) {
scoped<SessionData>()
scoped<UserPreferences>()
}
}

Annotations

@Scoped(MyActivityScope::class)
class Presenter(private val repository: UserRepository)

@Scoped(MyActivityScope::class)
class Navigator

Creating and Using Scopes

Manual Scope Management

// Create a scope
val myScope = getKoin().createScope("my_scope_id", named("session"))

// Get instances from scope
val sessionData: SessionData = myScope.get()
val prefs: UserPreferences = myScope.get()

// Close when done
myScope.close()

Android Activity Scope

class MyActivity : AppCompatActivity(), AndroidScopeComponent {
override val scope: Scope by activityScope()

// Scoped instances - created per Activity instance
private val presenter: Presenter by inject()

override fun onDestroy() {
super.onDestroy()
// Scope automatically closed
}
}

Android Fragment Scope

class MyFragment : Fragment(), AndroidScopeComponent {
override val scope: Scope by fragmentScope()

private val presenter: Presenter by inject()
}

Scope Types

Type-Based Scope

scope<MyActivity> {
scoped<ActivityPresenter>()
}

The scope is identified by the type MyActivity.

Named Scope

scope(named("user_session")) {
scoped<SessionManager>()
}

Use when the scope isn't tied to a specific type.

Qualifier-Based Scope

scope(named<MyQualifier>()) {
scoped<ScopedService>()
}

Scope Linking

Link scopes to access parent scope definitions:

val appModule = module {
// Activity scope
scope<MainActivity> {
scoped<ActivityData>()
}

// Fragment scope linked to Activity
scope<UserFragment> {
scoped<FragmentPresenter>()
}
}
class UserFragment : Fragment(), AndroidScopeComponent {
override val scope: Scope by fragmentScope()

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

// Link to parent Activity scope
scope.linkTo((requireActivity() as AndroidScopeComponent).scope)

// Now can access both Fragment and Activity scoped instances
val fragmentPresenter: FragmentPresenter by inject()
val activityData: ActivityData by inject() // From linked scope
}
}

Scope Source

Inject dependencies that are aware of their scope:

class Presenter(
val scope: Scope // Injected by Koin
) {
fun clearScope() {
scope.close()
}
}

scope<MyActivity> {
scoped { Presenter(get()) } // Scope injected
}

Scope Instance ID

Each scope instance has a unique ID:

// Create with explicit ID
val scope1 = getKoin().createScope("scope_1", named("session"))
val scope2 = getKoin().createScope("scope_2", named("session"))

// Different instances, same scope type
scope1.get<SessionData>() !== scope2.get<SessionData>()

Accessing Scoped Instances

From Within Scope

class MyActivity : AppCompatActivity(), AndroidScopeComponent {
override val scope: Scope by activityScope()

// Directly inject scoped instances
private val presenter: Presenter by inject()
}

From Outside Scope

// Get or create scope
val myScope = getKoin().getOrCreateScope("my_id", named("session"))

// Get instance
val session: SessionData = myScope.get()

In Compose

@Composable
fun MyScreen() {
// Create scope tied to Composable lifecycle
val scope = rememberKoinScope(named("screen_scope"))

// Get scoped instance
val presenter: ScreenPresenter = scope.get()
}

Scope Lifecycle

Closing Scopes

When a scope closes:

  1. All scoped instances are released
  2. onClose callbacks are invoked
  3. Scope becomes unusable
val scope = getKoin().createScope("my_scope", named("session"))

// Use the scope
val data: SessionData = scope.get()

// Close when done
scope.close() // SessionData instance released

// This throws an exception
// scope.get<SessionData>() // Error: Scope is closed

onClose Callback

scope(named("session")) {
scoped {
SessionData()
} onClose {
it?.cleanup() // Called when scope closes
}
}

Android Scope Components

Activity Scope

class MainActivity : AppCompatActivity(), AndroidScopeComponent {
override val scope: Scope by activityScope()

// Scope closed automatically in onDestroy()
}

Fragment Scope

class MyFragment : Fragment(), AndroidScopeComponent {
override val scope: Scope by fragmentScope()

// Scope closed automatically in onDestroy()
}

ViewModel Scope (4.1+)

startKoin {
options {
viewModelScopeFactory() // Enable ViewModel scopes
}
}

val appModule = module {
viewModel<UserViewModel>()

scope<UserViewModel> {
scoped<UserCache>()
}
}

Common Patterns

Session Scope

val appModule = module {
scope(named("user_session")) {
scoped { SessionManager() }
scoped { UserPreferences(get()) }
scoped { CartRepository(get()) }
}
}

// Login
fun onLogin(userId: String) {
val sessionScope = getKoin().createScope(userId, named("user_session"))
// Session instances now available
}

// Logout
fun onLogout(userId: String) {
getKoin().getScopeOrNull(userId)?.close()
// Session instances released
}

Feature Scope

val appModule = module {
scope(named("checkout")) {
scoped { CheckoutNavigator() }
scoped { CheckoutPresenter(get()) }
}
}

class CheckoutActivity : AppCompatActivity(), AndroidScopeComponent {
override val scope: Scope by lazy {
getKoin().createScope("checkout_${hashCode()}", named("checkout"))
}

override fun onDestroy() {
super.onDestroy()
scope.close()
}
}

Best Practices

  1. Use singletons sparingly - Only for truly app-wide dependencies
  2. Scope shared state - When multiple components need the same instance
  3. Close scopes explicitly - Don't rely on garbage collection
  4. Keep scopes focused - Don't put everything in one scope
  5. Use Android scope components - For automatic lifecycle management

Next Steps