Understanding OkHttp: Request Flow, Dispatcher, Interceptors, Connection Reuse, and Design Patterns
This article provides a comprehensive overview of OkHttp, covering its overall request process, the role of the dispatcher, how application and network interceptors work, TCP connection reuse via the connection pool, idle‑connection cleanup, key advantages, and the design patterns employed in the framework.
OkHttp is one of the most widely used HTTP client libraries in Android development, known for its ease of use, extensibility, and powerful features, making it a frequent topic in technical interviews.
OkHttp Request Overall Flow
A simple request involves creating an OkHttpClient and a Request , then calling newCall(request).enqueue(...) . Although the code appears straightforward, the framework performs extensive internal processing.
val okHttpClient = OkHttpClient()
val request: Request = Request.Builder()
.url("https://www.google.com/")
.build()
okHttpClient.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) { }
override fun onResponse(call: Call, response: Response) { }
})The request first passes through the dispatcher, which manages queues and thread pools, then proceeds through a chain of interceptors that handle retries, caching, connection establishment, and more.
OkHttp Dispatcher
The dispatcher maintains a running queue and a ready queue. For synchronous calls it simply records the request; for asynchronous calls it limits concurrent requests (default max 64 overall, max 5 per host) and schedules execution via a thread pool.
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call)
executorService().execute(call)
} else {
readyAsyncCalls.add(call)
}
}OkHttp Interceptors
After dispatching, the request traverses five default interceptors forming a responsibility chain. Application interceptors run first (user‑defined request modifications) and network interceptors run later (e.g., connection handling, response processing).
internal fun getResponseWithInterceptorChain(): Response {
val interceptors = mutableListOf
()
interceptors += client.interceptors
interceptors += RetryAndFollowUpInterceptor(client)
interceptors += BridgeInterceptor(client.cookieJar)
interceptors += CacheInterceptor(client.cache)
interceptors += ConnectInterceptor
if (!forWebSocket) interceptors += client.networkInterceptors
interceptors += CallServerInterceptor(forWebSocket)
val chain = RealInterceptorChain(this, interceptors, 0)
return chain.proceed(originalRequest)
}The chain ensures each interceptor can process or short‑circuit the request, with application interceptors typically used for logging and network interceptors for low‑level data handling.
TCP Connection Reuse
OkHttp’s ConnectInterceptor manages TCP connections. It first tries to reuse an existing connection, then checks the connection pool, and finally creates a new RealConnection if needed, supporting HTTP/1.1 keep‑alive and HTTP/2 multiplexing.
private RealConnection findConnection(...): RealConnection {
synchronized(connectionPool) {
// 1. Try assigned connection
// 2. Try pooled connection
// 3. Try pooled connection with routes (HTTP/2)
// 4. Create new connection and handshake
// 5. Final pool check for multiplexing
}
return result
}Idle Connection Cleanup
The connection pool runs a cleanup task that periodically scans connections, removes those idle longer than the keep‑alive timeout (default 5 minutes) or when the idle count exceeds the configured limit, and closes their sockets.
private long cleanup(long now) {
int inUse = 0, idle = 0
RealConnection longestIdle = null
long longestIdleDuration = Long.MIN_VALUE
synchronized(this) {
for (RealConnection conn : connections) {
if (pruneAndGetAllocationCount(conn, now) > 0) {
inUse++
continue
}
idle++
long idleDuration = now - conn.idleAtNanos
if (idleDuration > longestIdleDuration) {
longestIdleDuration = idleDuration
longestIdle = conn
}
}
if (longestIdleDuration >= keepAliveDurationNs || idle > maxIdleConnections) {
connections.remove(longestIdle)
} else if (idle > 0) {
return keepAliveDurationNs - longestIdleDuration
} else if (inUse > 0) {
return keepAliveDurationNs
} else {
cleanupRunning = false
return -1
}
}
closeQuietly(longestIdle.socket())
return 0
}Advantages of OkHttp
Simple API using the Builder pattern.
Highly extensible via custom application and network interceptors.
Supports multiple protocols (HTTP/1.1, HTTP/2, SPDY, WebSocket).
Connection pooling reduces latency.
Automatic GZIP compression.
Built‑in caching to avoid redundant network calls.
Automatic retries and redirects.
Design Patterns Used
Builder pattern for OkHttpClient and Request .
Facade pattern to expose a simple client interface.
Chain of Responsibility for the interceptor chain.
Flyweight (object pool) for TCP connections and thread pools.
The article concludes with a call for discussion, a QR‑code for a special gift, and links to additional resources.
Top Architect
Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.