skip to content

What kinds of objects can gradle.addListener accept, and how does Gradle decide which events it receives?

level: middleimportance: nice to knowfreq 22%

answer

  1. addListener inspects implemented interfaces
  2. one object → many event types
  3. TaskExecutionListener + TaskExecutionGraphListener etc.
  4. removeListener to unregister
  5. cache-incompatible mechanism

basics

~10 s

gradle.addListener takes one object and inspects which listener interfaces it implements — e.g. TaskExecutionListener, TaskExecutionGraphListener, ProjectEvaluationListener, BuildListener — and routes the matching events to it. One object can implement several.

solid answer

~40 s

`gradle.addListener(Object)` is a generic registration point. Gradle examines the supplied object's implemented listener interfaces and subscribes it to every event type it can handle. A single object may implement multiple interfaces — for instance a class implementing both `TaskExecutionListener` (per-task before/after) and `TaskExecutionGraphListener` (`graphPopulated`) will receive both kinds of callbacks. Recognized interfaces include `TaskExecutionListener`, `TaskExecutionGraphListener`, `ProjectEvaluationListener`, `BuildListener`, and `DependencyResolutionListener`. You remove a listener with `gradle.removeListener(obj)`. Because this whole mechanism relies on live mutable callbacks, it is configuration-cache incompatible; for execution observation the typed `OperationCompletionListener` route is preferred. `addListener` is the broad, one-call way to register cross-cutting observers when you're not targeting the configuration cache.

code

kotlin · 6 lines
kotlin
class Multi : TaskExecutionListener, TaskExecutionGraphListener {
    override fun graphPopulated(g: TaskExecutionGraph) { /* once */ }
    override fun beforeExecute(t: Task) {}
    override fun afterExecute(t: Task, s: TaskState) {}
}
gradle.addListener(Multi())  // receives both graph + per-task events

go deeper

for a junior

Know addListener takes an object and routes events based on which listener interfaces it implements.

for a middle

List the common interfaces it recognizes and that one object can implement several; mention removeListener.

for a senior

Note the cache incompatibility and steer execution-observation toward OperationCompletionListener.

for a principal

Discourage broad addListener-based observers in shared build logic; prefer narrowly-scoped, cache-safe services.

## A single registration, many event types `Gradle.addListener(Object listener)` is intentionally untyped. Rather than having a separate `addXListener` for every concern, Gradle inspects the object you pass and wires it up to **all listener interfaces it implements**. This is convenient: one object can be a one-stop observer. ## Interfaces it recognizes Among the listener interfaces routed through `addListener`: - `TaskExecutionListener` — `beforeExecute(task)` / `afterExecute(task, state)`. - `TaskExecutionGraphListener` — `graphPopulated(graph)` (graph readiness). - `ProjectEvaluationListener` — `beforeEvaluate` / `afterEvaluate` of projects. - `BuildListener` — coarse build phases (`settingsEvaluated`, `projectsLoaded`, `projectsEvaluated`, `buildFinished`). - `DependencyResolutionListener` — before/after dependency resolution of a configuration. If your object implements two of these, both sets of callbacks fire. ```kotlin class Observer : TaskExecutionListener, TaskExecutionGraphListener { override fun graphPopulated(graph: TaskExecutionGraph) { println("graph has ${graph.allTasks.size} tasks") } override fun beforeExecute(task: Task) {} override fun afterExecute(task: Task, state: TaskState) { println("${task.path}: didWork=${state.didWork}") } } gradle.addListener(Observer()) ``` ## Removing listeners `gradle.removeListener(obj)` unregisters the same object. There's rarely a need within a normal build, but long-lived tooling integrations may manage listener lifecycles explicitly. ## Scope Note the focus here is the *execution/graph* listeners: `TaskExecutionListener`, `TaskExecutionGraphListener`, and the `addListener` mechanism itself. Build-completion details (`buildFinished`) and project-evaluation details are handled by adjacent hooks; `addListener` simply happens to be the common door for several of them. ## Caveat All of these are live mutable callbacks — **not configuration-cache compatible**. For execution observation in cache-enabled builds, use `OperationCompletionListener` via `BuildEventsListenerRegistry` instead.

  • If an object implements TaskExecutionListener and TaskExecutionGraphListener, which fires first?
    graphPopulated fires once at the end of configuration; then beforeExecute/afterExecute fire repeatedly during execution. So the graph callback precedes any per-task callback.
  • How do you unregister a listener added with addListener?
    Call gradle.removeListener(theSameObject).

saying these in an interview costs you the question

  • Thinking addListener requires you to specify the event type — it infers it from implemented interfaces.
  • Assuming an object can only be one listener type at a time.

context