skip to content

What is addTaskExecutionGraphListener and how does it relate to whenReady?

level: middleimportance: should knowfreq 30%

answer

  1. graphPopulated == same moment as whenReady
  2. listener = reusable object, closure = sugar
  3. TaskExecutionGraphListener single method
  4. NOT TaskExecutionListener (per-task, deprecated)
  5. build services replace per-task listeners

basics

~10 s

addTaskExecutionGraphListener registers a TaskExecutionGraphListener whose graphPopulated method fires when the graph is ready — the same moment as whenReady. whenReady is just closure-based sugar for the same hook.

solid answer

~40 s

`gradle.taskGraph.addTaskExecutionGraphListener(listener)` registers an explicit `TaskExecutionGraphListener`. Its `graphPopulated(TaskExecutionGraph)` callback fires at exactly the same point as `whenReady` — when the task execution graph has been built but before execution. The difference is purely ergonomic: `whenReady { graph -> }` is a convenient closure/Action registration, while the listener is a reusable object implementing an interface, useful when you want to share one listener instance across builds, unit-test it, or also implement other listener callbacks. Functionally both observe the graph-ready event and receive the same `TaskExecutionGraph`. The listener form historically also exposed `graphPopulated`; some older listener interfaces (like `TaskExecutionListener` for per-task beforeTask/afterTask) are different and are deprecated/removed in favor of build services and the configuration-cache-friendly APIs.

code

kotlin · 4 lines
kotlin
gradle.taskGraph.addTaskExecutionGraphListener { graph ->
    // graphPopulated body via SAM conversion
    println("About to execute: " + graph.allTasks.joinToString { it.path })
}

go deeper

for a junior

Know that addTaskExecutionGraphListener and whenReady do the same thing, just different syntax.

for a middle

Name the graphPopulated method and explain when a reusable listener object beats an inline closure.

for a senior

Distinguish it from deprecated per-task TaskExecutionListener and point to build services as the replacement.

for a principal

Frame the choice in terms of plugin testability, configuration-cache compatibility, and the build-event listener API direction.

## Two ways to observe graph-ready Gradle exposes the same graph-populated event through two registration styles on `TaskExecutionGraph`: ### Closure / Action sugar ```kotlin gradle.taskGraph.whenReady { graph -> /* ... */ } ``` Concise; ideal for one-off build-script logic. ### Explicit listener ```kotlin class PlanLogger : TaskExecutionGraphListener { override fun graphPopulated(graph: TaskExecutionGraph) { println("Scheduled ${graph.allTasks.size} tasks") } } gradle.taskGraph.addTaskExecutionGraphListener(PlanLogger()) ``` The interface `TaskExecutionGraphListener` has a single method `graphPopulated(TaskExecutionGraph)`. It fires at the **identical** lifecycle moment as `whenReady`. ## When to prefer the listener - You have a **reusable, testable object** (e.g. shipped in a plugin) rather than inline script logic. - You want to register the same observer from plugin code with clear typing. - You need to keep a reference to remove or reason about it. ## Important distinction: not TaskExecutionListener Do **not** confuse `TaskExecutionGraphListener` (graph-ready, one shot) with the older per-task `TaskExecutionListener` (`beforeExecute`/`afterExecute` for *each* task). The per-task execution listeners are **deprecated/removed** in modern Gradle because they are incompatible with the configuration cache; the recommended replacements are **build services** (`BuildService`) and `OperationCompletionListener` via the build-event API. The graph-ready hook (`whenReady` / `addTaskExecutionGraphListener`) remains the supported way to react once to the populated graph. ## Same data, same timing Whichever you choose, you receive the same `TaskExecutionGraph` and can call `allTasks` and `hasTask`. The choice is about code organization, not capability.

  • Does graphPopulated fire before or after whenReady closures?
    They observe the same graph-ready event; both run at that boundary. Don't rely on a strict ordering between independently registered callbacks for correctness.
  • Why are per-task TaskExecutionListener beforeExecute/afterExecute discouraged?
    They are incompatible with the configuration cache and were deprecated/removed; build services and the build-event OperationCompletionListener are the supported replacements.

saying these in an interview costs you the question

  • Confusing TaskExecutionGraphListener (graph-ready, once) with TaskExecutionListener (per-task, deprecated).
  • Claiming the listener fires at a different lifecycle point than whenReady — it's the same graphPopulated moment.

context