DependencyGraph
Declares the annotated type to be a dependency graph. Metro's compiler plugin will build, validate, and implement this graph at compile-time.
Graph types must be either an interface or an abstract class.
Scoping
See Scope before reading this section!
Graphs may declare a scope (and optionally additionalScopes if there are more). Each of these declared scopes act as an implicit SingleIn representation of that scope. For example:
@DependencyGraph(AppScope::class)
interface AppGraph
Is functionally equivalent to writing the below.
@SingleIn(AppScope::class)
@DependencyGraph(AppScope::class)
interface AppGraph
Creating a graph
For simple graphs with no creator types, an implicit one will be generated. You can instantiate them with createGraph.
val graph = createGraph<AppGraph>()
For creators (more below), you can create a factory with createGraphFactory.
val graph = createGraphFactory<AppGraph.Factory().create("hello!")
Providers
Graph types can declare providers via Provides and Binds to provide dependencies into the graph.
Graph creators can provide instance dependencies and other graphs as dependencies.
@DependencyGraph
interface AppGraph {
val httpClient: HttpClient
@Provides fun provideHttpClient: HttpClient = HttpClient()
}
Creators
Graphs can have creators. Right now, this just means Factory creators. See its doc for more details.
Aggregation
Graphs can automatically aggregate contributed bindings and interfaces. Any contributions to the same scope will be automatically aggregated to this graph. This includes contributions generated from ContributesTo (supertypes), ContributesBinding, ContributesIntoSet, and ContributesIntoMap.
@DependencyGraph(AppScope::class)
interface AppGraph
@ContributesTo(AppScope::class)
interface HttpClientProvider {
val httpClient: HttpClient
}
@ContributesBinding(AppScope::class)
@Inject
class RealHttpClient(...) : HttpClient
// Results in generated code like...
@DependencyGraph(AppScope::class)
interface AppGraph : HttpClientProvider {
/* fake */override val RealHttpClient.bind: HttpClient
}
Graph Extensions
Dependency graphs can be marked as extendable to allow child graphs to extend them. These are similar in functionality to Dagger’s Subcomponents but are detached in nature like in kotlin-inject.
A graph must opt itself into extension in via marking isExtendable to true, which will make the Metro compiler generate extra metadata for downstream child graphs.
Then, a child graph can add an @Extends-annotated parameter to its creator to extend that graph.
@DependencyGraph(isExtendable = true)
interface AppGraph {
@Provides fun provideHttpClient(): HttpClient { ... }
}
@DependencyGraph
interface UserGraph {
@DependencyGraph.Factory
fun interface Factory {
fun create(@Extends appGraph: AppGraph): UserGraph
}
}
Child graphs then contain a superset of bindings they can inject, including both their bindings and their parents'. Graph extensions can be chained as well.
Child graphs also implicitly inherit their parents’ scopes.
Hoisting unused scoped class injections in parent graphs
In some cases, there are scoped bindings that are unused in the parent graph but are used in child graphs. Due to the detached nature of graph extensions, these bindings by default end up scoped to the child. To enforce that these bindings are scoped and held by the parent, Metro generates hints for these classes and discovers them during graph processing by default. You can disable this via the enableScopedInjectClassHints
property in the Gradle DSL.
Note that this must be enabled in the compilation of the scoped class itself.
Properties
Optional list of included binding containers. See the doc on BindingContainer for more details.
If enabled, marks this graph as available for extension and generates extra metadata about this graph's available bindings for child graphs to read.