Anvil Documentation

repository·main·Indexed 23 days ago

https://github.com/square/anvil

A Kotlin compiler plugin that simplifies Dagger dependency injection by automating the merging of modules and component interfaces into a central component using scopes. It provides annotations like @ContributesTo, @MergeComponent, @MergeSubcomponent, @ContributesBinding, and @ContributesMultibinding to reduce boilerplate. Anvil also supports custom code generation via the CodeGenerator interface and can generate Dagger Factory classes to accelerate build times by removing the need for KAPT in certain modules.

Tokens
6.1K
Snippets
18
Records
31
Agent score
80%

What's inside Anvil

  1. What is Anvil and how does it work?

    main

    Anvil is a Kotlin compiler plugin designed to simplify dependency injection with Dagger. It automates the process of merging Dagger modules and component interfaces into a central component.

    Instead of manually adding every module to a Dagger @Component or having a component extend multiple interfaces, you use Anvil annotations to 'contribute' modules and interfaces to a specific scope. Anvil then automatically generates the Dagger code that includes these contributions.

    Core Workflow:

    1. Define a Scope (a marker class).
    2. Use @ContributesTo(Scope::class) on modules or interfaces.
    3. Use @MergeComponent(Scope::class) on your main component interface.

    Anvil handles the boilerplate of connecting these pieces based on their shared scope.

    @Module
    @ContributesTo(AppScope::class)
    class DaggerModule { .. }
    
    @ContributesTo(AppScope::class)
    interface ComponentInterface {
      fun getSomething(): Something
      fun injectActivity(activity: MyActivity)
    }
    
    // The real Dagger component.
    @MergeComponent(AppScope::class)
    interface AppComponent
  2. How Scopes work in Anvil

    main

    Scopes in Anvil are simply marker classes used to link contributions to a specific component. They do not need to be functional; they just need to be unique identifiers.

    Example Scope Definition:

    abstract class AppScope private constructor()

    Important: Anvil scopes are independent of Dagger's own scoping annotations (like @Singleton). You still need to apply Dagger scopes to your component:

    @Singleton
    @MergeComponent(AppScope::class)
    interface AppComponent
    abstract class AppScope private constructor()
    
    @Singleton
    @MergeComponent(AppScope::class)
    interface AppComponent
  3. Limitations of Anvil code generators

    main

    When deciding whether to use Anvil's CodeGenerator API, be aware of the following constraints:

    • No modification/removal: Anvil code generators can only generate new code. They cannot modify or remove existing source code. If you need to modify existing code, you must use a Kotlin compiler plugin.
    • Not a replacement for KSP/KAPT: Anvil generators are not a replacement for Java annotation processing (KAPT) or Kotlin Symbol Processing (KSP). If you need to generate code independently of Anvil, use those tools instead.
  4. How Anvil merges contributions using @Merge*

    main

    Anvil automates Dagger 2 setup by merging contributed modules, bindings, component interfaces, and subcomponents. This process is triggered by @Merge* annotations (e.g., @MergeComponent(Scope::class)).

    The Merging Process:

    1. Frontend: Anvil generates "hints" in a special anvil.hint package for every contribution. This makes the scanning phase significantly faster (reducing overhead from ~20% to 2-4%).
    2. Backend: The actual merging happens in the compiler backend. Anvil modifies the annotated interface (e.g., adding @Component and including contributed modules/super types).

    Note on Visibility: Because merging happens in the backend, the resulting code is not visible as source code in the IDE. You can inspect the generated bytecode in .class or .jar files, or check the generated Java stubs in build/tmp/kapt3/stubs if using KAPT.

    @MergeComponent(Scope::class)
    interface MyComponent
  5. Anvil vs Hilt

    main

    Hilt is Google's opinionated dependency injection guide for Android and provides functionality similar to Anvil via the @InstallIn annotation. If your project uses Hilt, you do not need Anvil.

    Anvil is often used in large-scale codebases where migrating to Hilt is infeasible due to the number of modules, or where developers need to restrict the Dagger annotation processor to specific modules for performance reasons.

  6. Understand Anvil terminology

    main

    Key concepts used within Anvil's internal architecture:

    • KAPT: Kotlin's annotation processing tool. Anvil integrates with KAPT to ensure generated code is visible to the Dagger 2 annotation processor.
    • Hint: A pointer to a contributed class and its scope, stored in the anvil.hint package. Hints allow Anvil to find contributions without scanning the entire classpath.
    • CodeGenerator: An API interface that allows third parties to generate Kotlin code during the compiler frontend phase.
    • ClassReference: An abstraction over the Kotlin compiler's PSI and Descriptor APIs, allowing Anvil to work across different compilation stages without branching code.
    • PSI (Program Structure Interface): The abstract syntax tree used in the compiler frontend to represent source code.
    • Descriptors: Representations of resolved types used in the frontend. Unlike PSI, descriptors are stable once resolved and can represent Kotlin, Java, or bytecode.
    • IR (Intermediate Representation): The structure used in the compiler backend. The frontend translates PSI/Descriptors into IR, which is then transformed into platform-specific code (like JVM bytecode).
  7. Anvil Language and Configuration Limitations

    main

    Language Support

    Anvil is a Kotlin compiler plugin and does not support Java. However, you can use Anvil in mixed-language modules to provide Anvil functionality for your Kotlin classes.

    KAPT Configuration

    Anvil automatically sets correctErrorTypes to false in KAPT. This is necessary because enabling correctErrorTypes changes the invocation order of compiler plugins and KAPT, which prevents Anvil from correctly merging supertypes before the Dagger annotation processor runs.

    Incremental Compilation and Build Caching

    Anvil supports incremental compilation and Gradle build caching (enabled by default since v2.5.0).

    To ensure Anvil correctly merges contributions from module dependencies during incremental compilation, Anvil disables incremental compilation specifically for the stub generating task. This workaround ensures that changes in a dependency module are detected even if the module using @MergeComponent hasn't changed. This does not impact normal Kotlin compilation.

  8. How contributed bindings and subcomponents are merged

    main

    Unlike contributed modules and component interfaces which are merged in the backend, contributed bindings and subcomponents are merged in the frontend using a CodeGenerator.

    This is done because the generated code itself uses Anvil annotations that must be processed by other code generators. The CodeGenerator for contributed bindings scans the classpath to find all contributed bindings in the module and generates a single large Dagger module containing all the necessary binding or provides methods.

  9. Enable Dagger Factory Generation for build performance

    main

    Enabling Dagger Factory Generation can significantly improve build times by allowing you to remove the Dagger annotation processor and potentially KAPT.

    Anvil processes annotations like @Inject and @Module and generates the necessary Kotlin code directly. This avoids the slow KAPT stub generation task and the need to run javac for Dagger's Java code generation.

    Important Constraints:

    • @Component: If your module uses @Component, you must continue to use KAPT and the Dagger annotation processor. Anvil does not replicate the dependency verification and subcomponent generation logic performed by Dagger for components.
    • @MergeComponent: Modules using @MergeComponent must also enable KAPT.
    • Subcomponents: If a module exclusively uses subcomponents (and not @Component), you can safely remove the Dagger annotation processor and KAPT.
  10. Install Anvil via Gradle

    main

    Anvil consists of a Gradle plugin and a Kotlin compiler plugin. The Gradle plugin automatically manages the Kotlin compiler plugin and necessary annotation dependencies. You must apply the plugin to every module that either contributes classes to the dependency graph or merges them.

    Using the plugins block (Recommended):

    plugins {
      id 'com.squareup.anvil' version "${latest_version}"
    }

    Using the legacy buildscript method:

    buildscript {
      repositories {
        mavenCentral()
      }
      dependencies {
        classpath "com.squareup.anvil:gradle-plugin:${latest_version}"
      }
    }
    
    apply plugin: 'com.squareup.anvil'
  11. Configure the Anvil Gradle Plugin

    main

    Anvil includes a Gradle plugin that handles the setup of the Kotlin compiler plugin. It provides an anvil { .. } DSL for configuration and manages the inclusion of the annotations artifact on the compile classpath. It also applies necessary workarounds for incremental builds (e.g., addressing KT-38576).

    To integrate custom code generators, add the generator project as an anvil dependency in your dependencies block.

    dependencies {
      anvil project(':sample:code-generator')
      implementation project(':sample:annotation')
    }