What are the semantics of gradle.sharedServices.registerIfAbsent, and why is it the recommended way to register a BuildService from a plugin?
answer
- gradle.sharedServices registry
- idempotent on name (IfAbsent)
- returns Provider, not the instance
- lazy: no instance until first use
- stable unique name, plugin-prefixed
basics
~20 sregisterIfAbsent registers a named build service if one with that name doesn't already exist, and returns its Provider either way. That makes it idempotent, so multiple plugins can register the same service safely without conflicts.
solid answer
~50 s`gradle.sharedServices.registerIfAbsent(name, type) { ... }` registers a `BuildService` under a unique `name` and returns a `Provider<Service>`. Its defining property is **idempotency**: if a service with that name is already registered, it returns the existing registration instead of failing or creating a duplicate. This is why plugins use it — several plugins (or the same plugin applied to many projects) can all call `registerIfAbsent` with the same name and the same type and safely converge on one shared service, rather than racing to create conflicting registrations. Registration is **lazy**: it does not instantiate the service; the instance is created on first use of the returned provider during execution. The configuration lambda is where you'd set parameters (covered by the authoring topic). For the shared-state lifecycle role, the key points are: unique name, returns a Provider, idempotent, lazy instantiation, build-scoped single instance.
code
kotlin · 7 lines// Called from a plugin applied to many projects — safe to repeat
val provider = gradle.sharedServices.registerIfAbsent(
"com.acme.cache", // unique, stable name
CacheService::class.java
) {
// configure parameters here (authoring concern)
}go deeper
Know it registers a named service and gives back a Provider you pass to tasks.
Explain idempotency on the name and lazy instantiation, and why both help plugins share one instance.
Discuss naming strategy across plugins and how idempotency avoids duplicate/competing registrations.
Define naming conventions (plugin-id-prefixed) so independent plugins deliberately share or isolate services without collisions.
## The API ```kotlin val provider: Provider<MyService> = gradle.sharedServices.registerIfAbsent("my-service", MyService::class.java) { // optional: configure parameters here } ``` `gradle.sharedServices` is the build-scoped `BuildServiceRegistry`. `registerIfAbsent` takes a **unique name**, the service **type**, and a configuration action, and returns a `Provider<MyService>`. ## Idempotency — the headline behavior The `IfAbsent` in the name is the whole point. If no service with the given name exists, it registers a new one. If one **already exists**, it returns the **existing** registration without re-registering or erroring. This matters because: - A plugin may be **applied to many projects**; each application calls `registerIfAbsent`, but you want a single shared service, not N of them. - **Multiple independent plugins** may want the same shared resource; agreeing on a name lets them share one instance. Without idempotency you'd get duplicate-name errors or competing instances. ## Laziness Registration is cheap and **does not create the service object**. The `Provider` is a promise; the service is instantiated only when a running task first calls `.get()`. So registering a service that no task uses costs essentially nothing and never triggers `close()`. ## Name uniqueness The name is the identity key. Two `registerIfAbsent` calls with the **same name** are treated as the same service (and should use the same type/params); a different name yields a different service. Choose stable, descriptive, collision-resistant names (often prefixed by the plugin id). ## What lives elsewhere Setting parameters in the configuration lambda and declaring `maxParallelUsages` are part of the authoring API; the migration of existing globals to services for the configuration cache is a separate topic. Here the registration semantics — **idempotent, lazy, Provider-returning, build-scoped** — are what enable safe shared state.
- What happens if two plugins call registerIfAbsent with the same name?They converge on one shared registration — the second call returns the existing one rather than creating a duplicate or failing.
- Does registerIfAbsent create the service immediately?No. It is lazy; the instance is created on first use of the returned Provider during task execution.
saying these in an interview costs you the question
- Saying registerIfAbsent instantiates the service right away.
- Claiming duplicate names create separate instances (they converge to one).
- Using non-unique or unstable names that collide across plugins.