What is ProviderFactory in Gradle, and how do you obtain it inside a plugin or task?
answer
- service that builds lazy Providers
- project.providers or @Inject
- environmentVariable / systemProperty / gradleProperty
- deferred, config-cache-aware reads
- don't hold Project in a task
basics
~10 sProviderFactory 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 linesabstract class MyTask @Inject constructor(
private val providers: ProviderFactory
) : DefaultTask() {
@get:Input
val region: Provider<String> = providers.environmentVariable("AWS_REGION")
}go deeper
Name it as the source of lazy Providers and mention project.providers plus the env/sysprop/gradleProperty methods.
Explain @Inject access in tasks and why laziness matters; mention orElse chaining of sources.
Tie it to configuration-cache correctness and not holding Project references in tasks.
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