Setting up OkHttp for Network Requests in Android Application
OkHttp is the HTTP client on which Retrofit, Coil, Glide and most Android libraries working with network are built. Use OkHttp directly when you need full control: WebSocket connections, custom protocols, file uploads with progress, specific header handling — things either impossible in Retrofit or requiring workarounds.
When OkHttp Directly, Not Through Retrofit
WebSocket: OkHttpClient.newWebSocket(request, listener) — native support without extra dependencies. WebSocketListener receives callbacks onOpen, onMessage, onFailure, onClosed. For automatic reconnect you need own logic with exponential backoff.
File upload/download with progress. Retrofit allows uploading via @Multipart, but tracking progress only via custom RequestBody wrapping source and calling callback on byte write. This is OkHttp level.
Custom authentication. Authenticator interface OkHttp calls on 401, allows synchronously getting new token and retrying request. Works with Retrofit too, but via OkHttpClient.
Shared HTTP client for multiple libraries. Coil accepts OkHttpClient in ImageLoader.Builder, Retrofit in Retrofit.Builder. One configured client with shared connection pool and cache instead of several separate clients with duplicated logic.
OkHttpClient Configuration
val okHttpClient = OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.cache(Cache(cacheDir, 10 * 1024 * 1024)) // 10 MB cache
.addInterceptor(authInterceptor)
.addInterceptor(loggingInterceptor)
.addNetworkInterceptor(networkMonitorInterceptor)
.authenticator(tokenRefreshAuthenticator)
.connectionPool(ConnectionPool(5, 5, TimeUnit.MINUTES))
.build()
addInterceptor vs addNetworkInterceptor: application-interceptors called once and see cached responses. Network-interceptors only for real network requests. For logging byte traffic — network interceptor. For adding auth headers — application interceptor.
HTTP cache: Cache with correct cache-dir (usually context.cacheDir) speeds up repeat requests and works offline if server sends Cache-Control headers. If not — ForceCacheInterceptor with forced FORCE_CACHE for offline.
Certificate pinning: CertificatePinner.Builder().add("api.example.com", "sha256/AAAA...").build(). SHA-256 fingerprint obtained from cert via openssl x509 -in cert.pem -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | base64. Pinning breaks on certificate rotation — must add new fingerprint before change.
Typical Mistakes
Creating OkHttpClient on every request. Client holds thread pool, connection pool and cache — must be singleton. In Hilt — @Singleton in @Provides.
Blocking operations inside Interceptor. Interceptor.intercept() runs on OkHttp dispatcher threads, but if doing suspend function via runBlocking — blocks dispatcher thread. For async operations in interceptor (e.g., token refresh) use synchronized block with condition variable, or move refresh to Authenticator which is synchronous by contract.
Testing: MockWebServer from com.squareup.okhttp3:mockwebserver — standard tool for network layer unit-tests. Starts local server, accepts requests, returns prepared responses.
OkHttp setup with interceptors, cache, WebSocket or file uploads — 1–3 days. Cost calculated individually.







