skip to content

What is ProviderFactory in Gradle, and how do you obtain it inside a plugin or task?

level: juniorimportance: must knowfreq 55%

answer

  1. service that builds lazy Providers
  2. project.providers or @Inject
  3. environmentVariable / systemProperty / gradleProperty
  4. deferred, config-cache-aware reads
  5. don't hold Project in a task

basics

~10 s

ProviderFactory creates lazy Provider values from sources like environment variables, system properties and Gradle properties. You get it via project.providers, or by @Inject in a task/plugin.

solid answer

~40 s

`ProviderFactory` is the service that produces lazy `Provider<T>` values from external inputs — environment variables (`providers.environmentVariable("X")`), system properties (`providers.systemProperty`), Gradle properties (`providers.gradleProperty`), and external processes (`providers.exec`, `providers.of(ValueSource)`). Each call returns a `Provider` that is only read when queried (e.g. `.get()`), so values stay lazy and deferred to execution time. You access it through `project.providers` in build scripts/plugins, or — for objects created by Gradle's instantiator (tasks, extensions, custom services) — by constructor injection: `@Inject constructor(providers: ProviderFactory)`. Using it instead of reading `System.getenv()` directly makes the inputs visible to the configuration cache, so the build re-runs when those inputs change rather than going stale.

code

kotlin · 6 lines
kotlin
abstract class MyTask @Inject constructor(
    private val providers: ProviderFactory
) : DefaultTask() {
    @get:Input
    val region: Provider<String> = providers.environmentVariable("AWS_REGION")
}

go deeper

for a junior

Name it as the source of lazy Providers and mention project.providers plus the env/sysprop/gradleProperty methods.

for a middle

Explain @Inject access in tasks and why laziness matters; mention orElse chaining of sources.

for a senior

Tie it to configuration-cache correctness and not holding Project references in tasks.

for a principal

Frame it as the single sanctioned channel for external inputs so the whole build's input graph is observable and cacheable.

## What ProviderFactory is `ProviderFactory` is a built-in Gradle service whose job is to wrap *external inputs* into lazy `Provider<T>` objects. A `Provider<T>` is a deferred value: it holds *how to compute* a value, not the value itself, and is only resolved when something calls `.get()`, `.getOrNull()`, or `.getOrElse(...)`. Reading inputs through `ProviderFactory` (rather than plain Java/Kotlin APIs like `System.getenv`) is what lets Gradle: - defer reads until they are actually needed (laziness), - track them as build inputs so the **configuration cache** invalidates correctly, - and chain/transform them with `map`/`orElse` without forcing eager evaluation. ## How to obtain it Two idiomatic ways: 1. **`project.providers`** — available anywhere you have a `Project` (build scripts, `Plugin.apply`). 2. **Constructor injection** — for types Gradle instantiates (tasks, extensions, `BuildService`s), annotate the constructor with `@Inject` and accept `ProviderFactory`. This is preferred inside tasks because you should never hold a `Project` reference in a task (it breaks the configuration cache). ## The core factory methods - `environmentVariable(name)` → `Provider<String>` - `systemProperty(name)` → `Provider<String>` - `gradleProperty(name)` → `Provider<String>` (reads `gradle.properties` / `-P`) - `provider { ... }` → wrap an arbitrary computation - `exec { ... }` / `of(ValueSource)` → config-cache-safe external/process reads ## Example ```kotlin abstract class PublishTask @Inject constructor( providers: ProviderFactory ) : DefaultTask() { @get:Input val token: Provider<String> = providers.environmentVariable("PUBLISH_TOKEN") .orElse(providers.gradleProperty("publishToken")) } ``` Here the token is read lazily and only at execution, and both sources are recorded as configuration-cache inputs.

  • Why inject ProviderFactory into a task instead of using project.providers?
    A task must not keep a reference to Project at execution time (it breaks the configuration cache). Injecting ProviderFactory gives the same capability without capturing Project.
  • When is the value of providers.environmentVariable actually read?
    Lazily — only when the Provider is queried with get()/getOrElse(). The factory just records intent until then.

saying these in an interview costs you the question

  • Saying ProviderFactory eagerly returns the current value
  • Claiming you should reach for project.providers from inside a task's execution

context