This guide is for engineers who need to understand how Retrofit works beneath the annotation surface: the runtime request pipeline, the converter/call-adapter SPI, the multi-module repository layout, the build and release system, and the library's security and operational characteristics. Where the User Guide explains how to use Retrofit, this guide explains how Retrofit is built — enough to extend it, integrate it deeply, debug release-only failures, or contribute to it.
Covers Retrofit at commit 8e83a24 (release line 3.0.0, development version 3.1.0-SNAPSHOT).
1. Executive Summary
Retrofit is a type-safe HTTP client for Android and the JVM, maintained by Square under the Apache 2.0 license. Its central idea: you declare an API as an annotated Java/Kotlin interface, and Retrofit generates a working implementation at runtime using a java.lang.reflect.Proxy. All actual networking is delegated to OkHttp; Retrofit's job is the adaptation layer — turning a method invocation into an OkHttp request, and an OkHttp response back into your declared type.
| Attribute | Value |
|---|---|
| Language | Java (core), with Kotlin (KotlinExtensions.kt) and Kotlin modules |
| License | Apache License 2.0 |
| Group / coordinates | com.squareup.retrofit2 |
| Current release | 3.0.0 (2025-05-15) |
| Minimum runtime | Java 8+ or Android API 21+ |
| Core runtime dependency | OkHttp 4.12 (api scope → transitive; brings transitive Kotlin) |
| Build system | Gradle (multi-module), Java toolchain pinned to JDK 8 language level |
| Distribution | Maven Central, multi-release JARs, a BOM |
| Module count | Core + 11 converter modules + 6 adapter modules + mock + BOM + response-type-keeper |
Architectural one-liner: a dynamic proxy resolves each interface method, once, into a cached ServiceMethod that pairs a parsed request template (RequestFactory) with a response converter and a call adapter; invoking the method builds an OkHttpCall and adapts it to the declared return type.
2. System Architecture
2.1 High-level request pipeline
Your interface (annotated)
│ retrofit.create(MyApi.class)
▼
java.lang.reflect.Proxy ── InvocationHandler
│ method invoked
▼
Retrofit.loadServiceMethod(service, method) ◄── ConcurrentHashMap cache (per Method)
│ (parse once, reuse forever)
▼
ServiceMethod.parseAnnotations
├── RequestFactory.parseAnnotations ── parses @GET/@Path/@Query/... into a request template
└── HttpServiceMethod.parseAnnotations
├── resolves a CallAdapter (from callAdapterFactories)
└── resolves a response Converter<ResponseBody, T> (from converterFactories)
│
▼
HttpServiceMethod.invoke(args)
└── new OkHttpCall<>(requestFactory, args, callFactory, responseConverter)
│
▼
CallAdapter.adapt(call) ──► Call<T> | Observable | CompletableFuture | suspend result
│
▼
okhttp3.Call.Factory (OkHttpClient) ── real HTTP I/O
2.2 The proxy and per-method caching — Retrofit.create
Retrofit.create(Class<T>) returns a Proxy.newProxyInstance whose InvocationHandler:
- Defers
java.lang.Objectmethods (equals,hashCode,toString) to normal invocation. - For Kotlin/Java default (interface) methods, invokes them directly via the platform's
Reflectionstrategy. - For everything else, calls
loadServiceMethod(service, method).invoke(proxy, args).
loadServiceMethod is the concurrency-critical core. It maintains a ConcurrentHashMap<Method, Object> whose values progress monotonically through three states:
- absent — no thread has begun parsing this method.
- a lock object — one thread is parsing; others block on that lock.
- a
ServiceMethod— parsing complete; the happy path returns it immediately.
The first thread to putIfAbsent a fresh lock wins, parses annotations, stores the result, and releases. On a parsing exception it removes the lock so other waiters retry (and observe the real exception, rather than a cryptic ClassCastException — a deliberate fix from 2.11.0). This makes interface parsing thread-safe and pay-once.
Note
Annotation parsing uses reflection and is comparatively expensive — which is exactly why each
ServiceMethodis built once and cached. Build yourRetrofitinstance and its proxies once and reuse them.
2.3 Parsing annotations — ServiceMethod → RequestFactory + HttpServiceMethod
ServiceMethod.parseAnnotations does two things:
- Builds a
RequestFactoryfrom the method's HTTP annotations — the reusable template that knows how to turnargsinto anokhttp3.Request(path substitution, query/header/field/part handlers, body converter selection). - Validates the return type (rejects
voidand unresolvable type variables/wildcards), then delegates toHttpServiceMethod.parseAnnotations.
HttpServiceMethod.parseAnnotations resolves the two pluggable pieces and chooses a concrete subclass:
| Subclass | When | Behavior |
|---|---|---|
CallAdapted |
Normal (non-suspend) methods | callAdapter.adapt(call) |
SuspendForResponse |
Kotlin suspend fun … : Response<T> |
awaitResponse(call, continuation) |
SuspendForBody |
Kotlin suspend fun … : T |
await / awaitNullable / awaitUnit |
For suspend functions, Retrofit synthesizes an adapter type of Call<ResponseType> (unwrapping Response<T> when present) and forces the SkipCallbackExecutor annotation so the coroutine isn't double-dispatched through the callback executor. It also rejects suspend fun returning Call<T> (always incorrect) and enforces that @HEAD uses Void/Unit.
Important
SuspendForBodydeliberately catches allThrowablesubtypes (except fatalVirtualMachineError/ThreadDeath/LinkageError) and routes them throughKotlinExtensions.suspendAndThrow. This avoids Java'sUndeclaredThrowableExceptionwhen a checked exception is thrown synchronously across the proxy boundary — a subtle but important correctness property of coroutine support.
2.4 Executing the request — OkHttpCall
HttpServiceMethod.invoke constructs an OkHttpCall<> that wraps the requestFactory, the invocation args, the okhttp3.Call.Factory, and the response converter, then hands it to the call adapter. OkHttpCall lazily creates the underlying okhttp3.Call, executes (execute()) or enqueues (enqueue()) it, applies the response converter to a successful body, and surfaces error bodies via Response.errorBody(). A single OkHttpCall is single-use; clone() produces a fresh one.
2.5 Platform abstraction — Platform
Platform is a static-initializer singleton that selects runtime strategies by inspecting System.getProperty("java.vm.name"):
| VM | Callback executor | Reflection strategy | Built-in factories |
|---|---|---|---|
Dalvik (Android), SDK ≥ 24 |
AndroidMainExecutor (main thread) |
Reflection.Android24 |
BuiltInFactories.Java8 |
Dalvik, SDK < 24 |
AndroidMainExecutor |
Reflection |
BuiltInFactories |
RoboVM |
none (synchronous) | Reflection |
BuiltInFactories |
| default JVM | none (synchronous) | Reflection.Java8 |
BuiltInFactories.Java8 |
This is why callbacks land on the Android main thread but on the calling background thread on the plain JVM, and why default-interface-method invocation differs by Java version (handled through a multi-release JAR — see §5.2).
2.6 Factory ordering — Retrofit.Builder.build
Order matters because Retrofit tries factories in list order until one returns non-null.
Converter factories are assembled as:
[ BuiltInConverters ] + [ your converters, in add order ] + [ platform default converters ]
BuiltInConverters is added first and cannot be overridden — this both prevents you from breaking core behavior (raw ResponseBody/RequestBody/Void/Unit handling) and ensures correct behavior when a user converter (Moshi, Gson) would otherwise greedily consume all types. The platform defaults at the tail include the Optional converters.
Call adapter factories are assembled as:
[ your adapters, in add order ] + [ platform default adapters ]
The defaults include DefaultCallAdapterFactory (plain Call<T>) and, on Java 8+, CompletableFutureCallAdapterFactory. The callback Executor (Android main thread, or null on the JVM) is threaded into the default call adapters at build time.
Tip
The resolution loop exposes a clean diagnostic: when no factory matches, Retrofit throws an exception that lists the factories it Skipped and Tried. Read that list to see exactly which converters/adapters were registered and in what order.
3. The Extension SPI
Retrofit's extensibility rests on two abstract factory classes. Every converter and adapter module in the repository is a thin implementation of one of them.
3.1 Converter.Factory
public interface Converter<F, T> {
@Nullable T convert(F value) throws IOException;
abstract class Factory {
public @Nullable Converter<ResponseBody, ?> responseBodyConverter(
Type type, Annotation[] annotations, Retrofit retrofit) { return null; }
public @Nullable Converter<?, RequestBody> requestBodyConverter(
Type type, Annotation[] parameterAnnotations,
Annotation[] methodAnnotations, Retrofit retrofit) { return null; }
public @Nullable Converter<?, String> stringConverter(
Type type, Annotation[] annotations, Retrofit retrofit) { return null; }
}
}
A factory overrides whichever of the three it supports and returns null for types it doesn't handle:
responseBodyConverter—ResponseBody→ your type (theCall<T>body).requestBodyConverter— your type →RequestBody(for@Body,@Part,@PartMap).stringConverter— your type →String(for@Field,@FieldMap,@Header,@HeaderMap,@Path,@Query,@QueryMap). When none matches, Retrofit falls back toBuiltInConverters.ToStringConverter(Object.toString()).
Helpers getParameterUpperBound(int, ParameterizedType) and getRawType(Type) (delegating to Utils) make generic-type inspection ergonomic.
3.2 CallAdapter.Factory
public interface CallAdapter<R, T> {
Type responseType(); // body type used to prepare the Call (e.g. Repo for Call<Repo>)
T adapt(Call<R> call); // wrap the Call into the target type
abstract class Factory {
public abstract @Nullable CallAdapter<?, ?> get(
Type returnType, Annotation[] annotations, Retrofit retrofit);
}
}
get(...) inspects the method's declared return type and returns an adapter, or null to defer to the next factory. responseType() tells Retrofit which body type to deserialize into — note it is typically not the same as the method's return type (for Single<User> the response type is User).
3.3 Worked example: a delegating converter
The Optional converters (Guava/Java 8) are the canonical "delegating" pattern — they don't parse bytes; they call retrofit.nextResponseBodyConverter(...) for the inner type and wrap a possibly-null result in an Optional. This is why an Optional converter must be registered before the converter that does the real parsing: it needs to delegate to it.
4. Repository Structure
The repository is a Gradle multi-module build. The core library is small; most modules are thin SPI implementations plus their test suites.
retrofit/ ← repository root
├── retrofit/ ← CORE library (retrofit2.*)
│ ├── src/main/java/retrofit2/ ← Retrofit, ServiceMethod, OkHttpCall, etc.
│ │ ├── http/ ← the 25 annotations (@GET, @Path, ...)
│ │ └── internal/ ← EverythingIsNonNull (internal-only)
│ ├── src/main/java14/ ← multi-release: Java 14 default-method reflection
│ ├── src/main/java16/ ← multi-release: Java 16 default-method reflection
│ ├── android-test/ ← Android instrumentation tests
│ ├── java-test/ kotlin-test/ ← JVM + Kotlin unit tests
│ ├── robovm-test/ ← RoboVM (iOS) platform test
│ └── test-helpers/
├── retrofit-converters/ ← gson, jackson, moshi, protobuf, wire,
│ │ simplexml, jaxb, jaxb3, kotlinx-serialization,
│ │ scalars, guava, java8
├── retrofit-adapters/ ← rxjava, rxjava2, rxjava3, guava, java8, scala
├── retrofit-mock/ ← MockRetrofit, BehaviorDelegate, NetworkBehavior
├── retrofit-bom/ ← bill-of-materials (java-platform)
├── retrofit-response-type-keeper/ ← annotation processor for shrinker keep-rules
├── samples/ ← runnable examples (SimpleService, Crawler, ...)
├── website/ ← Astro Starlight docs site (src/content/docs/*)
└── build.gradle / settings.gradle
Note
The authoritative prose documentation lives in
website/src/content/docs/(index.md,declarations.md,configuration.md,download.mdx,contributing.md). Thewebsite/public/tree contains generated Dokka/Javadoc HTML for published versions and is build output, not source.
4.1 Core type inventory (retrofit2)
| Type | Role |
|---|---|
Retrofit / Retrofit.Builder |
Configuration entry point + proxy factory |
ServiceMethod (abstract) |
Per-method parsed model; parseAnnotations |
RequestFactory |
Parsed HTTP-annotation template → okhttp3.Request |
HttpServiceMethod (+ CallAdapted, SuspendForResponse, SuspendForBody) |
Binds request template to converter + adapter; bridges coroutines |
OkHttpCall |
Call<T> backed by an okhttp3.Call |
Call / Callback / Response |
Public request/response API |
Converter / CallAdapter |
Extension SPI |
BuiltInConverters / BuiltInFactories |
Default conversions and factory sets |
DefaultCallAdapterFactory / CompletableFutureCallAdapterFactory |
Default adapters |
OptionalConverterFactory |
Public delegating converter for Optional |
Platform / Reflection / AndroidMainExecutor |
Runtime-platform strategy |
ParameterHandler / RequestBuilder |
Per-parameter request assembly |
Invocation |
Captures service Class, method, and arguments for interceptors |
HttpException |
Thrown for non-2xx responses on body-returning suspend calls |
KotlinExtensions.kt |
await/awaitResponse/suspendAndThrow coroutine bridge |
Utils |
Type utilities (raw type, bounds, validation) |
5. Build System
5.1 Gradle configuration
The root build.gradle applies cross-cutting configuration to all subprojects:
- Java toolchain language level 8 (
JavaLanguageVersion.of(8)) — the library targets Java 8 bytecode for maximum compatibility. - Error Prone with several checks escalated to
ERROR:MissingFail,MissingOverride,UnusedException,UnusedMethod,UnusedNestedClass,UnusedVariable,WildcardImport. - Spotless formatting:
googleJavaFormatfor Java,ktfmt(Google style) for Kotlin. - Animal Sniffer signature checks: every
java-libraryis verified against thejava18signature and (except the Java 8-specific converter/adapter modules) the Android API level 21 signature — this is how the library guarantees its "Java 8+ / Android API 21+" floor. - A
copyWebsiteDocstask assembles Dokka/Javadoc output intowebsite/public/3.xfor publishing.
5.2 Multi-release JAR
The core retrofit module compiles three source sets — main (Java 8), java14, and java16 — and packages the version-specific classes under META-INF/versions/{14,16} via a multi-release JAR. These hold the reflection needed to invoke interface default methods on newer JDKs. The JAR manifest sets Automatic-Module-Name: retrofit2 and Multi-Release: true. Each module sets its own automatic module name (e.g. retrofit2.converter.gson, retrofit2.adapter.rxjava3).
Caution
Static-analysis tools unaware of multi-release JARs may flag the Java 14/16 bytecode classes. This is expected and harmless; the correct class is selected by the runtime JDK.
5.3 Dependency scopes
The core module's build.gradle declares OkHttp as api (so it leaks transitively to consumers — intentional, since you interact with OkHttp types) and Android + kotlinx-coroutines as compileOnly (optional at runtime; present only when you use Android callbacks or coroutines). Each converter/adapter module declares api projects.retrofit plus its target library (api libs.gson, api libs.rxjava3, etc.), keeping the dependency graph explicit per module.
5.4 Testing matrix
The java-test module is configured to run the unit suite across every JDK from 8 through 24 (a testJdkN task per version, using Azul Zul toolchains), wiring each into check. There are dedicated Kotlin (kotlin-test), Android instrumentation (android-test), and RoboVM (robovm-test) suites so coroutine behavior, the Android main-thread executor, and the iOS/RoboVM platform path are all exercised.
6. Operations & Integration
Retrofit is a library, not a running service — "operations" here means integrating and operating it correctly inside an application.
6.1 Recommended integration shape
// One client, one Retrofit, reused process-wide.
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(Duration.ofSeconds(10))
.addInterceptor(authInterceptor)
.addInterceptor(loggingInterceptor)
.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.example.com/") // MUST end in /
.client(client)
.addConverterFactory(MoshiConverterFactory.create())
.addCallAdapterFactory(RxJava3CallAdapterFactory.create())
.validateEagerly(isDebugBuild) // surface bad declarations at startup
.build();
- Singletons. A
Retrofitinstance is immutable and thread-safe; share it. The connection pool and dispatcher live on theOkHttpClient. validateEagerly(true)parses and validates every method in the interface atcreate(...)time. Enable in debug/CI to catch malformed declarations at startup rather than on first call; leave off in production to avoid the upfront reflection cost.- Timeouts, retries, caching, auth all belong on the
OkHttpClient, not Retrofit.
6.2 Threading model
| Return type | Where work runs | Where the result is delivered |
|---|---|---|
Call.execute() |
Calling thread (blocks) | Calling thread |
Call.enqueue() |
OkHttp dispatcher thread | Android: main thread · JVM: the dispatcher thread |
RxJava Observable/Single/… |
Per RxJava subscribeOn/observeOn |
As scheduled |
CompletableFuture |
OkHttp dispatcher thread | Completing thread |
suspend fun |
OkHttp dispatcher thread | Resumes on the coroutine's dispatcher |
The Android main-thread callback behavior comes from AndroidMainExecutor selected in Platform; on the JVM there is no callback executor, so callbacks run on the background request thread.
6.3 Error handling model
- For
Call,Callback.onResponsefires for every HTTP response. CheckResponse.isSuccessful()(status in[200,300)); parseerrorBody()for failures.onFailurefires only for I/O or request/response processing errors. - For body-returning
suspendfunctions, a non-2xx response throwsHttpException(carryingcode(),message(), and the fullresponse()), while network failures throwIOException.
6.4 Performance characteristics
- First call per method pays reflection cost (annotation parsing); subsequent calls hit the
ServiceMethodcache. - Streaming.
@Streamingavoids buffering large response bodies into memory; the caller must consume and close theResponseBody. - Deferred serialization. Since 2.12.0, first-party converters (Gson, Jackson, Moshi, Protobuf, Wire) offer a
withStreaming()factory that defers request-body serialization to HTTP-write time — moving conversion off the calling thread when usingCall.enqueue.
7. Security Analysis
Retrofit is a client library with a deliberately small security surface; most concerns live in how you configure it and OkHttp.
| Area | Posture / guidance |
|---|---|
| Transport security | TLS, certificate pinning, and trust configuration are OkHttp's responsibility. Configure OkHttpClient (e.g. CertificatePinner) and pass it to .client(...). Always use https:// base URLs. |
| Credentials | Inject Authorization/API-key headers via an OkHttp Interceptor, not per-method @Header (avoids leaking secrets into many call sites and into logs). |
| URL encoding | @Path/@Query/@Field values are URL-encoded by default. Setting encoded = true opts out — only do so for values you have already correctly encoded, or you risk request-splitting/injection. |
| Header injection | allowUnsafeNonAsciiValues = true permits non-ASCII header values that are not strictly HTTP-compliant; use only when a server requires it, and never with untrusted input. |
| Untrusted input → URL | @Url lets the whole URL come from a runtime value. Validate/whitelist host and scheme when the value originates from user or server data, since a full URL on the endpoint can replace the base host entirely. |
| Deserialization | Body parsing is delegated to the chosen converter (Gson/Jackson/etc.). Deserialization-safety (polymorphic type handling, resource limits) is governed by that library's configuration, not Retrofit. |
| Shrinker correctness | R8 rules ship automatically; ProGuard requires manual rules (retrofit2.pro) or generic type info can be stripped, causing release-only failures (see §8). |
| Dependency provenance | Publications are signed (signAllPublications=true) and pushed to Maven Central; verify checksums/signatures in supply-chain-sensitive builds. |
Vulnerability reporting follows the repository's BUG-BOUNTY.md policy.
8. Troubleshooting (Operational)
IllegalArgumentException: Could not locate ResponseBody converter for <type> / … RequestBody converter …
The factory list had no match. The exception lists Skipped and Tried factories — confirm the converter is registered and ordered correctly (delegating/Optional converters before greedy ones like Moshi/Gson). Remember BuiltInConverters is always first and immovable.
IllegalArgumentException: Could not locate call adapter for <type>
No CallAdapter.Factory handled the return type. Register the matching adapter (addCallAdapterFactory(...)). The default factories only handle Call<T> and (Java 8+) CompletableFuture<T>.
Release build fails with Method return type must not include a type variable or wildcard or Unable to create converter
Shrinking stripped generic signatures. Ensure R8 (rules automatic), or add retrofit2.pro for ProGuard plus OkHttp/Okio rules. For interfaces whose runtime type needs exceed static rules, use the retrofit-response-type-keeper annotation processor.
ClassCastException from a second caller after a parsing failure
Fixed in 2.11.0 — the parsing exception is now visible to all concurrent callers, not just the first. If you observe this, you are on a pre-2.11 version; upgrade.
UndeclaredThrowableException from a suspend function
Should not occur on 3.x — SuspendForBody/SuspendForResponse intercept synchronous throwables and route them through the continuation. If seen, verify you are not wrapping the suspend call in a custom proxy that bypasses this handling.
JAXB converter ignores @XmlJavaTypeAdapter
Open issue #3349 in JaxbConverterFactory. Track upstream; work around by relocating the adapter to a binding level the JAXB implementation honors.
9. Extending Retrofit
9.1 Writing a converter factory
public final class YamlConverterFactory extends Converter.Factory {
public static YamlConverterFactory create() { return new YamlConverterFactory(); }
@Override public Converter<ResponseBody, ?> responseBodyConverter(
Type type, Annotation[] annotations, Retrofit retrofit) {
return body -> yaml.loadAs(body.charStream(), (Class<?>) type);
}
@Override public Converter<?, RequestBody> requestBodyConverter(
Type type, Annotation[] p, Annotation[] m, Retrofit retrofit) {
return value -> RequestBody.create(yaml.dump(value), MediaType.get("application/yaml"));
}
}
Return null from any method for types you don't handle so Retrofit can try the next factory. Install with addConverterFactory(YamlConverterFactory.create()).
9.2 Writing a call adapter factory
Extend CallAdapter.Factory, inspect returnType in get(...), and return a CallAdapter whose responseType() is the body type and whose adapt(Call<R>) wraps the call into your target type (delegating to call.execute()/call.enqueue(...)). The built-in RxJava/Guava/Java 8 adapters are the reference implementations; the samples/ module includes ErrorHandlingAdapter and DynamicBaseUrl as worked extension examples.
9.3 Reading request metadata with Invocation
Invocation (attached to the OkHttp request as a tag) exposes the service Class<?>, the invoked Method, and the arguments. An OkHttp interceptor can read it for logging, metrics, or routing — samples/InvocationMetrics.java demonstrates per-endpoint timing. The newer Invocation.annotationUrl returns the original URL declared on the method annotation.
10. Contributing
Contribution flow (from website/src/content/docs/contributing.md):
- Fork the repository and open a pull request from a branch.
- Follow existing conventions and style — formatting is enforced by Spotless (
googleJavaFormat/ktfmtGoogle style). - Ensure the build passes:
./gradlew build(gradlew.bat buildon Windows). This runs Error Prone, Animal Sniffer signature checks, Spotless, and the multi-JDK test matrix. - Sign the Individual Contributor License Agreement before code can be merged.
Release & distribution
gradle.propertiespinsGROUP=com.squareup.retrofit2; the development version is3.1.0-SNAPSHOT.- Publishing uses
com.vanniktech.maven.publishwithmavenCentralPublishing=true,mavenCentralAutomaticPublishing=true, andsignAllPublications=true. - In-development snapshots are published to the Central Portal Snapshots repository.
- The
retrofit-bommodule publishes a constraints-only platform so consumers can align all module versions.
11. Version & Compatibility Notes
| Aspect | Detail |
|---|---|
| Current release | 3.0.0 (2025-05-15) — upgraded to Kotlin-based OkHttp 4.12 (from 3.14) |
| Binary compatibility | 3.x is forward binary-compatible with 2.x; 2.x-compiled libraries run on 3.x |
| Transitive Kotlin | Introduced in 3.0 via OkHttp 4.12 |
| Recent additions (Unreleased) | Explicit keep rules for RxJava Result types; allowoptimization flags; Invocation.annotationUrl |
| 2.12.0 | Streaming serialization (withStreaming()) for Gson/Jackson/Moshi/Protobuf/Wire; primitive @Tag support |
| 2.11.0 | OptionalConverterFactory made public; concurrent parsing-exception visibility fix |
| 2.10.0 | kotlinx.serialization + JAXB 3 converters; BOM; Unit response type; non-ASCII header values; response-type-keeper processor |
12. Quick Reference
| Need | API |
|---|---|
| Build a client | new Retrofit.Builder().baseUrl(url).build() |
| Add JSON support | .addConverterFactory(GsonConverterFactory.create()) |
| Add a return-type adapter | .addCallAdapterFactory(RxJava3CallAdapterFactory.create()) |
| Custom HTTP client | .client(okHttpClient) |
| Validate at startup | .validateEagerly(true) |
| Derive a variant config | retrofit.newBuilder()…build() |
| Generate an implementation | retrofit.create(MyApi.class) |
| Resolve a converter manually | retrofit.responseBodyConverter(type, annotations) |
| Resolve an adapter manually | retrofit.callAdapter(returnType, annotations) |
| Inspect registered factories | retrofit.converterFactories() / retrofit.callAdapterFactories() |
| Single-use call → fresh copy | call.clone() |
| Non-2xx as exception (coroutines) | catch HttpException |