3 of 5
Scopes
One instance per scope — shared within a context, isolated between contexts.
Basic scoped bindings
Scoped bindings create one instance per scope — a singleton within a lifecycle boundary. Useful when a dependency must be shared within a context (like an HTTP request) but isolated between contexts.
container.scoped<DbConnection> { DbConnection(resolve<Config>()) }
// Scoped bindings can only be resolved within a scope:
container.resolve<DbConnection>() // throws ScopeRequiredException
val scope = container.child()
scope.resolve<DbConnection>() // creates instance
scope.resolve<DbConnection>() // same instance
scope.close() // instance disposedBlock-based scopes
Use the block syntax for automatic cleanup — the scope closes when the block ends:
container.scope { scope ->
val db = scope.resolve<DbConnection>()
// use db...
} // scope auto-closes hereDispose hooks
Attach onClose to run cleanup when the scope closes:
container.scoped<DbConnection> { DbConnection() }
.onClose { it.disconnect() }Instances implementing AutoCloseable are closed automatically — no hook needed:
container.scoped<InputStream> { FileInputStream("data.bin") }
// .close() called automatically when scope closesIf both onClose and AutoCloseable apply, only the explicit onClose runs.
Nested scopes
Scopes can be nested. Each scope gets its own scoped instances, and closing a parent cascades to children (deepest first):
container.scope { outer ->
val outerDb = outer.resolve<DbConnection>()
outer.scope { inner ->
val innerDb = inner.resolve<DbConnection>() // different instance
// inner closes first
}
// outer closes after
}Scopes as child containers
A scope is a full Container — you can register ad-hoc bindings on it:
container.scope { scope ->
scope.singleton<RequestId> { RequestId.generate() }
scope.resolve<RequestHandler>() // can depend on RequestId
}Contextual scopes
Use service providers to set up different scope contexts. The scope's purpose is defined by what you register on it:
// HTTP request scope
fun handleRequest(container: Container, request: HttpRequest) {
container.scope { scope ->
scope.register(RequestScopeProvider(request))
scope.resolve<RequestHandler>().handle()
}
}
// Background job scope
fun processJob(container: Container, job: Job) {
container.scope { scope ->
scope.register(JobScopeProvider(job))
scope.resolve<JobProcessor>().run()
}
}Android
In Android, the system controls Activity and Fragment lifecycles — you can't wrap them in a scope { } block. Instead, tie scopes to lifecycle callbacks:
class MyActivity : AppCompatActivity() {
private lateinit var scope: Scope
override fun onCreate(savedInstanceState: Bundle?) {
scope = appContainer.child()
scope.register(ActivityScopeProvider(this))
val presenter = scope.resolve<Presenter>()
}
override fun onDestroy() {
scope.close()
super.onDestroy()
}
}Next steps
Learn how to organize registrations into reusable modules with service providers.