Retrofit 与 OkHttp 全面详解
OkHttp 全面详解
1. 为什么选择 OkHttp?
它的核心优势包括:
-
连接池复用:对于 HTTP/1.x,它会复用同一个主机的多个请求共享同一个 Socket 连接,有效降低延迟。对于 HTTP/2,则通过多路复用进一步优化 。
-
拦截器机制:灵活可扩展的责任链模式
-
透明 GZIP 压缩:默认添加
Accept-Encoding: gzip头并自动处理解压,减少传输数据量,提升速度 。 -
缓存机制:支持 HTTP 协议标准的缓存机制,可以完全避免重复的网络请求,直接使用缓存数据,减少网络请求 。
-
自动重试与重定向:当网络出现问题时,它会自动重试;遇到 301/302 等重定向状态码时,也会自动跟进 。
-
简洁的 API:其强大的
Builder设计模式使得构造复杂的请求变得非常简单 -
HTTP/2 支持:多路复用提升性能
-
异步支持:回调或协程
-
配置灵活:丰富的配置选项
2. OkHttp 核心架构
OkHttp 核心组件:
├── OkHttpClient(客户端,共享配置)
├── Request(请求构建)
├── Response(响应封装)
├── RealCall(实际请求执行)
├── Dispatcher(调度器,管理请求队列)
├── ConnectionPool(连接池,复用连接)
└── Interceptor(拦截器,责任链模式)
3. 快速入门:基本使用
OkHttp 的使用流程非常清晰,主要分为四步:创建客户端 -> 构建请求 -> 发起请求 -> 处理响应
3.1 get请求
// 创建请求
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://api.example.com/data")
.build();
try {
// 同步执行 execute() 会阻塞
Response response = client.newCall(request).execute();
// 4. 处理响应
if (response.isSuccessful()) {
// 注意:response.body().string() 只能调用一次
String responseData = response.body().string();
System.out.println(responseData);
} else {
System.out.println("请求失败,状态码:" + response.code());
}
} catch (IOException e) {
e.printStackTrace();
}
// 异步执行
client.newCall(request).enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) {
// 请求成功,在子线程中执行
if (response.isSuccessful()) {
String responseData = response.body().string();
// 注意:如果需要更新 UI,请切换到主线程
// runOnUiThread(() -> textView.setText(responseData));
}
}
@Override
public void onFailure(Call call, IOException e) {
// 请求失败,在子线程中执行
e.printStackTrace();
}
});
3.2 post请求
POST 请求需要构造 RequestBody 来传递参数。
// 表单形式提交
OkHttpClient client = new OkHttpClient();
RequestBody formBody = new FormBody.Builder()
.add("username", "example_user")
.add("password", "example_password")
.build();
Request request = new Request.Builder()
.url("https://api.example.com/login")
.post(formBody)
.build();
client.newCall(request).enqueue(callback);
4. OkHttpClient 深度配置
val client = OkHttpClient.Builder()
// 1. 超时配置(为了防止网络问题导致应用卡死,合理配置超时是必要的)
.connectTimeout(10, TimeUnit.SECONDS) // 连接超时
.readTimeout(30, TimeUnit.SECONDS) // 读取超时
.writeTimeout(30, TimeUnit.SECONDS) // 写入超时
.callTimeout(60, TimeUnit.SECONDS) // 整个调用超时(API 4.0.0+)
// 2. 连接池配置
// 每个 OkHttpClient 对象都有自己的连接池和线程池。应该将 OkHttpClient 实例设计为单例
// 全局复用,以充分发挥连接复用的优势,避免不必要的资源开销
.connectionPool(ConnectionPool(
maxIdleConnections = 5, // 最大空闲连接数
keepAliveDuration = 5, // 保持活动时间(分钟)
timeUnit = TimeUnit.MINUTES
))
// 3. 拦截器
// 通过添加 HttpLoggingInterceptor 可以方便地查看请求和响应的详细日志,非常利于调试
.addInterceptor(LoggingInterceptor()) // 应用拦截器
.addNetworkInterceptor(NetworkInterceptor()) // 网络拦截器
// 4. 缓存(默认缓存是关闭的,需要手动配置)
.cache(Cache(
directory = File(context.cacheDir, "okhttp_cache"),
maxSize = 10 * 1024 * 1024L // 10MB
))
// 5. DNS
.dns(Dns.SYSTEM) // 默认系统DNS
// 或自定义DNS
.dns(object : Dns {
override fun lookup(hostname: String): List<InetAddress> {
// 自定义DNS解析逻辑
return Dns.SYSTEM.lookup(hostname)
}
})
// 6. 代理
.proxy(Proxy(Proxy.Type.HTTP, InetSocketAddress("proxy.com", 8080)))
.proxyAuthenticator(ProxyAuthenticator())
// 7. Cookie管理
.cookieJar(object : CookieJar {
private val cookieStore = mutableMapOf<String, List<Cookie>>()
override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) {
cookieStore[url.host] = cookies
}
override fun loadForRequest(url: HttpUrl): List<Cookie> {
return cookieStore[url.host] ?: emptyList()
}
})
// 8. SSL/TLS配置
.sslSocketFactory(sslContext.socketFactory, trustManager)
.hostnameVerifier { hostname, session ->
// 主机名验证逻辑
HostnameVerifier.DEFAULT.verify(hostname, session)
}
// 9. 协议配置
.protocols(listOf(Protocol.HTTP_1_1, Protocol.HTTP_2))
// 10. 事件监听器(用于监控)
.eventListener(object : EventListener() {
override fun callStart(call: Call) {
Log.d("OkHttp", "Call started: ${call.request().url}")
}
override fun callEnd(call: Call) {
Log.d("OkHttp", "Call ended: ${call.request().url}")
}
})
.build()
5. 核心思想:拦截器与责任链
OkHttp 最精妙的设计在于其拦截器(Interceptor)机制。它基于责任链设计模式,将网络请求的各个阶段(如重试、缓存、连接、数据收发)解耦成一个个独立的拦截器,按顺序执行 。
一个请求的完整流程就像一条生产线,每个拦截器负责一道工序
5.1 内置拦截器及其顺序
当我们调用 getResponseWithInterceptorChain() 方法时,OkHttp 会按顺序添加一系列拦截器 :
-
RetryAndFollowUpInterceptor:负责处理失败重试和重定向。如果请求失败,它会进行重试;如果收到重定向响应,它会创建新的请求并重新走一遍流程 。 -
BridgeInterceptor:桥接拦截器。它负责将用户构建的Request转换为能够进行网络传输的请求,比如添加Content-Length、Host、User-Agent等头部信息,并处理 GZIP 解压 。 -
CacheInterceptor:缓存拦截器。它负责读取缓存和更新缓存。如果缓存命中且未过期,直接返回缓存数据,不再发起网络请求 。 -
ConnectInterceptor:连接拦截器。它负责与服务器建立连接。它会在连接池中寻找可用的连接,如果没有则创建一个新的连接(包括 TCP 握手和 TLS 握手)。 -
CallServerInterceptor:请求服务拦截器。它是链条的最后一环,负责向服务器发送请求数据,并读取服务器的响应数据 。
除了这些内置拦截器,我们还可以在客户端配置时添加两种自定义拦截器:
-
应用拦截器 (
addInterceptor):最早被执行,总会被执行一次,可以操作原始的请求和最终的响应。 -
网络拦截器 (
addNetworkInterceptor):在ConnectInterceptor之后,CallServerInterceptor之前执行。它可以观察每一次重定向和重试过程中的中间数据,并且总能获取到非空的Connection对象
5.2 拦截器链的执行顺序
请求方向:应用拦截器 → 网络拦截器 → 服务器
响应方向:服务器 → 网络拦截器 → 应用拦截器
5.3 官方内置拦截器顺序
// OkHttp 内部拦截器链的构建(RealCall.java)
Response getResponseWithInterceptorChain() throws IOException {
// 构建拦截器链
List<Interceptor> interceptors = new ArrayList<>();
// 1. 用户添加的应用拦截器(最先执行)
interceptors.addAll(client.interceptors());
// 2. 内置重试和重定向拦截器
interceptors.add(new RetryAndFollowUpInterceptor(client));
// 3. 内置桥接拦截器
interceptors.add(new BridgeInterceptor(client.cookieJar()));
// 4. 内置缓存拦截器
interceptors.add(new CacheInterceptor(client.internalCache()));
// 5. 内置连接拦截器
interceptors.add(new ConnectInterceptor(client));
// 6. 用户添加的网络拦截器
if (!client.networkInterceptors().isEmpty()) {
interceptors.addAll(client.networkInterceptors());
}
// 7. 内置网络调用拦截器(最后一个)
interceptors.add(new CallServerInterceptor(
client.readAndWriteTimeout().isEnabled()));
// 构建链并执行
Interceptor.Chain chain = new RealInterceptorChain(
interceptors, transmitter, null, 0, originalRequest,
client.connectTimeoutMillis(), client.readTimeoutMillis(),
client.writeTimeoutMillis());
return chain.proceed(originalRequest);
}
5.4 请求阶段(Request ):
用户代码
↓
应用拦截器1 → 对Request预处理
↓
应用拦截器2
↓
...
↓
应用拦截器N
↓
RetryAndFollowUpInterceptor → 处理重试/重定向
↓
BridgeInterceptor → 添加必要Headers
↓
CacheInterceptor → 检查缓存
↓
ConnectInterceptor → 建立连接
↓
网络拦截器1
↓
网络拦截器2
↓
...
↓
网络拦截器N
↓
CallServerInterceptor → 实际网络请求
↓
服务器
5.4 响应阶段(Response Phase):
服务器
↓
CallServerInterceptor ← 获取原始响应
↓
网络拦截器N ← 可修改响应
↓
...
↓
网络拦截器1
↓
ConnectInterceptor
↓
CacheInterceptor ← 缓存响应
↓
BridgeInterceptor ← 移除桥接Headers
↓
RetryAndFollowUpInterceptor ← 处理重试逻辑
↓
应用拦截器N ← 可修改最终响应
↓
...
↓
应用拦截器2
↓
应用拦截器1
↓
用户代码
5.5 应用拦截器 vs 网络拦截器
|
特性 |
应用拦截器 | 网络拦截器 |
| 添加位置 | .addInterceptor() |
.addNetworkInterceptor() |
| 执行时机 | 请求最早/响应最晚 | 发送请求前/接收响应后 |
| 重定向/重试 | 每个请求只调用一次 (即使重定向/重试) |
每次网络调用都会执行 (重定向/重试会多次调用) |
| 连接信息 | 无网络信息 | 可获得网络相关连接信息 |
| Headers | 可添加所有headers | 只包含网络headers |
关键逻辑:
-
链按顺序执行拦截器
-
每个拦截器调用
chain.proceed(request)传递给下一个 -
最后一个拦截器(CallServerInterceptor)实际发起网络请求
-
响应逆序返回给各个拦截器
总结
正确顺序:
|
阶段 |
拦截器类型 |
执行顺序 |
|---|---|---|
|
请求阶段 |
应用拦截器 |
1 → 2 → ... → N |
|
内置拦截器 |
按固定顺序 |
|
|
网络拦截器 |
1 → 2 → ... → N |
|
|
CallServerInterceptor |
最后(实际网络请求) |
|
|
响应阶段 |
CallServerInterceptor |
最先(获取原始响应) |
|
网络拦截器 |
N → ... → 2 → 1 |
|
|
内置拦截器 |
逆序 |
|
|
应用拦截器 |
N → ... → 2 → 1 |
6. OkHttp 核心源码解析
6.1 请求执行流程
RealCall.execute() / enqueue()
↓
Dispatcher.dispatch() // 分发请求
↓
AsyncCall.run() // 异步执行
↓
getResponseWithInterceptorChain() // 拦截器链
↓
RetryAndFollowUpInterceptor // 重试和重定向
↓
BridgeInterceptor // 桥接拦截器
↓
CacheInterceptor // 缓存拦截器
↓
ConnectInterceptor // 连接拦截器
↓
CallServerInterceptor // 网络拦截器
↓
Response // 返回响应
-
入口:
Call的创建:OkHttpClient.newCall(request)实际上创建了一个RealCall对象。 -
执行:
execute()或enqueue()-
如果是同步,直接调用
getResponseWithInterceptorChain()。 -
如果是异步,
RealCall.enqueue()会将请求交给Dispatcher(分发器)处理。Dispatcher内部维护了线程池和等待队列,负责并发控制(默认最大并发请求 64 个,单个主机最大 5 个)。
-
-
核心:
getResponseWithInterceptorChain():这是最核心的方法。它创建一个拦截器链,并将上述提到的所有拦截器添加到列表中,然后启动链条,让请求依次通过每个拦截器。 -
递归调用:
RealInterceptorChain.proceed():链条的运作机制是递归的。每个拦截器在intercept()方法中处理完自己的逻辑后,会调用chain.proceed(request),将请求传递给下一个拦截器,直到最后一个拦截器执行真正的 I/O 操作并返回Response。Response再沿着链条逆向传递回来,被每个拦截器处理(例如CacheInterceptor会将结果写入缓存)
6.2 常用拦截器实现
(1) 日志拦截器
class LoggingInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val startTime = System.nanoTime()
// 打印请求信息
Log.d("OkHttp", "--> ${request.method} ${request.url}")
request.headers.forEach { name, value ->
Log.d("OkHttp", "$name: $value")
}
if (request.body != null) {
val buffer = Buffer()
request.body!!.writeTo(buffer)
Log.d("OkHttp", "Body: ${buffer.readUtf8()}")
}
// 继续执行
val response = chain.proceed(request)
val endTime = System.nanoTime()
// 打印响应信息
Log.d("OkHttp", "<-- ${response.code} ${response.message} " +
"${request.url} (${(endTime - startTime) / 1_000_000}ms)")
response.headers.forEach { name, value ->
Log.d("OkHttp", "$name: $value")
}
return response
}
}
(2) 重试拦截器
class RetryInterceptor(
private val maxRetries: Int = 3,
private val delayMillis: Long = 1000
) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
var lastException: IOException? = null
for (retryCount in 0..maxRetries) {
try {
return chain.proceed(chain.request())
} catch (e: IOException) {
lastException = e
if (retryCount == maxRetries) {
break
}
// 指数退避
val waitTime = delayMillis * (1 shl retryCount)
Log.d("OkHttp", "Retry $retryCount after ${waitTime}ms")
Thread.sleep(waitTime)
}
}
throw lastException ?: IOException("Unknown error")
}
}
(3) Token 自动添加拦截器
class AuthInterceptor(private val tokenProvider: () -> String?) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val originalRequest = chain.request()
val token = tokenProvider()
val request = if (token != null) {
originalRequest.newBuilder()
.header("Authorization", "Bearer $token")
.build()
} else {
originalRequest
}
var response = chain.proceed(request)
// Token 过期处理(401)
if (response.code == 401) {
synchronized(this) {
val newToken = refreshToken() // 刷新token
if (newToken != null) {
val newRequest = originalRequest.newBuilder()
.header("Authorization", "Bearer $newToken")
.build()
response.close()
response = chain.proceed(newRequest)
}
}
}
return response
}
private fun refreshToken(): String? {
// 刷新token的逻辑
return null
}
}
(4) 缓存拦截器
class OfflineCacheInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
var request = chain.request()
val cacheControl = request.cacheControl
// 强制网络请求
if (!isNetworkAvailable()) {
request = request.newBuilder()
.cacheControl(CacheControl.FORCE_CACHE)
.build()
}
val response = chain.proceed(request)
return if (isNetworkAvailable()) {
// 有网络:设置缓存策略
val maxAge = 60 * 60 // 1小时
response.newBuilder()
.removeHeader("Pragma")
.header("Cache-Control", "public, max-age=$maxAge")
.build()
} else {
// 无网络:使用缓存
val maxStale = 60 * 60 * 24 * 7 // 1周
response.newBuilder()
.removeHeader("Pragma")
.header("Cache-Control",
"public, only-if-cached, max-stale=$maxStale")
.build()
}
}
private fun isNetworkAvailable(): Boolean {
// 检查网络连接
return true
}
}
(5) 响应处理拦截器
// 统一响应处理拦截器
class ResponseInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val response = chain.proceed(request)
return when (response.code) {
in 200..299 -> {
// 成功响应
response
}
in 400..499 -> {
// 客户端错误
handleClientError(response)
}
in 500..599 -> {
// 服务器错误
handleServerError(response)
}
else -> {
// 其他错误
handleUnknownError(response)
}
}
}
private fun handleClientError(response: Response): Response {
when (response.code) {
401 -> throw UnauthorizedException("认证失败")
403 -> throw ForbiddenException("无权访问")
404 -> throw NotFoundException("资源不存在")
429 -> throw RateLimitException("请求过于频繁")
else -> throw ApiException(response.code, "客户端错误")
}
}
}
7. 面试常见问题
Q1: OkHttp 的拦截器执行顺序?
A: 应用拦截器 → 重试拦截器 → 桥接拦截器 → 缓存拦截器 → 连接拦截器 → 网络拦截器 → 服务器
Q2: OkHttp 如何实现连接复用?
A: 通过 ConnectionPool 管理空闲连接,相同的 Address 可以复用连接,HTTP/2 支持多路复用。
Q3: OkHttp 的缓存机制?
A: 基于 HTTP 缓存规范,支持内存和磁盘缓存,通过 CacheInterceptor 实现。
Q4: 如何取消 OkHttp 请求?
A: 调用 Call.cancel()方法,会中断网络请求。
Q5: OkHttp 如何处理重定向?
A: 通过 RetryAndFollowUpInterceptor 自动处理 3xx 重定向,默认最多 20 次。
Q6: 如何监控网络请求?
A: 使用 EventListener 监听请求生命周期事件。
Q7: OkHttp 如何保证线程安全?
A: 通过 Dispatcher 管理请求队列,ConnectionPool 线程安全,RealCall 使用 synchronized。
Q8: OkHttp 与 Retrofit 的关系?
A: Retrofit 是基于 OkHttp 的 RESTful 客户端,OkHttp 处理网络层,Retrofit 处理接口层。
8. 网络请求流程分析
使用内部工厂类Builder来设置OkHttpClient
OkHttpClient.Builder builder = new0kHttpClient.Builder();
builder.connectTimeout(60,TimeUnit.sECONDS) // 设置超时
.addInterceptor(interceptor) // 添加拦截器
.proxy(proxy) // 设置请求代理
.cache(cache); // 设置缓存策略
OkHttpClient client = builder.build();
请求操作的起点从OkHttpClient.newCall().enqueue()方法开始
newCall返回一个RealCall对象,并将网络请求添加到请求队列中
@override
public Call newCall(Request request) {
return RealCall.newRealCall(this,request,false);
}
RealCall.enqueue()

Dispatcher 是OkHttpClient的调度器,是一种门户模式
主要用来实现执行、取消异步请求操作
本质上是内部维护了一个线程池去执行异步操作
保证最大并发个数、同一host 主机允许执行请求的线程个数
Dispatcher 的 enqueue方法的具体实现:实际上就是使用线程池执行了一个AsyncCall,AsyncCall实现了一个runnable接口,所以操作会在子线程执行
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
真正获取请求结果的方法getResponseWithInterceptorChain,可以看出是一个拦截器的调用链
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
interceptors.add(retryAndFollowUpInterceptor);
interceptors.add(newBridgeInterceptor(client.cookieJar()));
interceptors.add(newCacheInterceptor(client.internalCache()));
interceptors.add(newConnectInterceptor(client));
if (!forWebSocket) {
interceptors.addA1l(client.networkInterceptors());
}
interceptors.add(newCallserverInterceptor(forWebSocket));
Interceptor.Chain chain = new RealInterceptorChain(interceptors,null, null,null, 0, originalRequest,this,eventListener,client.connectTimeoutMillis(), client.readTimeoutMillis(),client.writeTimeoutMillis());
return chain.proceed(originalRequest);
}
每一个拦截器的作用:
BridgeInterceptor:主要对Request中的Head 设置默认值,比如Content-Type、Keep-Alive、Cookie 等
CacheInterceptor:负责HTTP 请求的缓存处理
ConnectInterceptor:负责建立与服务器地址之间的连接,也就是TCP链接
CallServerInterceptor:负责向服务器发送请求,并从服务器拿到远端数据结果
9. Cachelnterceptor缓存拦截器
Cachelnterceptor主要做以下几件事情:
1. 根据Request获取当前已有缓存的Response(有可能为 null),并根据获取到的缓存Response,创建CacheStrategy对象
//如果开发人员配置了自定义的缓存:优先从自定义缓存中读取Response
Response cacheCandidate = cache != null ? cache.get(chain.request()) : null;
// 创建缓存策略,通过它来判断某缓存是否有效
CacheStrategy strategy = new CacheStrategy.Factory(now,chain.request(),cacheCandidate).get();
Request networkRequest = strategy.networkRequest;
Response cacheResponse = strategy.cacheResponse;
2. 通过 CacheStrategy判断当前缓存中的Response 是否有效(比如是否过期)
// 如果缓存无效,且禁止使用网络请求,则直接返回空的Response
if(networkRequest == null && cacheResponse == null) {
return new Response.Builder()
。。。
.code(504)
.message("Unsatisfiable Request(only-if-cached)")
.body(Util.EMPTY_RESPONSE) // 空的body
。。。
.build();
}
// 缓存在有效期内,则将缓存中的Response返回
if (networkRequest == null){
return cacheResponse.newBuilder( )
.cacheResponse(stripBody(cacheResponse))
.build();
}
// 最后如果没有缓存,或者缓存失效,则发送网络请求获取服务器端Response
Response networkResponse = null;
try { // 执行下一个拦截器,发起网路请求
networkResponse = chain.proceed(networkRequest);
} finally {
。。。
}
3. 如果从服务器端成功获取Response,再判断是否将此Response进行缓存操作
// 通过网络请求获取最新Response数据
Response response = networkResponse.newBuilder( )
.cacheResponse(stripBody(cacheResponse) )
.networkResponse(stripBody(networkResponse))
.build();
// 如果开发人员有设置自定义cache,则将最新的数据缓存起来
if (cache != null) {
if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response,networkRequest)) {
CacheRequest cacheRequest . cache.put(response);
return cacheWritingResponse(cacheRequest,response);
}
// 将response返回(有可能来自缓存,也有可能是来自网络请求)
return response;
10. 通过Cache 实现缓存功能
OkHttp 只是规范了一套缓存策略
但是具体使用何种方式将数据缓存到本地,以及如何从本地缓存中取出数据
都是由开发人员自己定义并实现,并通过OkHttpClient.Builder的cache方法设置
OkHttp提供了一个默认的缓存类Cache.java,可以在构建OkHttpClient时,直接使用Cache来实现缓存功能,只需要指定缓存路径和最大可用空间

Cache 内部使用了DiskLruCach 实现具体的缓存功能
11. CallServerInterceptor详解
CallServerInterceptor是 OkHttp 中最后一个拦截器,也是 OkHttp 中最核心的网路请求部分

12. OkHttp 使用扩展
Okio 是 Square公司打造的另外一个轻量级10库,它是OkHttp框架的基石
在构建Response时,需要调动body()方法传入一个ResponseBody对象
ResponseBody内部封装了对请求结果的流读取操作,可以通过继承并扩展ResponseBody的方式获取网络请求的进度
OkHttp 使用扩展--继承ResponseBody

OkHttp 使用扩展--自定义 ProgressBarClient
private static OkHttpclient client;
public static synchronized OkHttpClient getclient() {
if (client == null) {
final File cacheDir =MyApplication.getInstance().getExternalCacheDir();
client = new 0kHttpClient.Builder( )
.cache(new Cache(new File(cacheDir,"okhttp"),60*1024 * 1024))
.build();
}
return client;
}
/**
* Not singleton
*/
private static 0kHttpClient getProgressBarClient(final ProgressListener listener) {
return getclient().newBuilder().addNetworkInterceptor(new Interceptor() {
@override
public Response intercept(Chain chain) throws IOException {
Response originalResponse = chain.proceed(chain.request());
return originalResponse.newBuilder( )
.body(new ProgressResponseBody(originalResponse.body( ),listener))
.build();
}
}).bui1d();
getClient 可以根据项目的不同添加其他共通设置,比如timeout 时间、DNS、Log日志interceptor等
getProgressBarClient 通过添加一个拦截器,并且在intercept方法中将自定义的ProgressResponseBody传给body方法
实践拓展-Picasso
Picasso 是Square公司研发用来从网络端获取图片数据的依赖库,内部实质上是使用OkHttp来实现请求操作的
eg:获取图片下载进度,后续只要通过getPicasso 方法即可获得一个自带下载进度的Picasso对象

// 实现一个带进度提示的图片下载效果
PieImageView pieImageView=((PieImageView) findViewById(R.id.pieImageView));
Picasso imagePicasso =SquareUtils.getPicasso(this,listener);
imagePicasso.load("http://app.xrlaxs.com/upload/image/20190426/1556244587404435.jpg")
.placeholder (pieImageView.getDrawable())
.config(Bitmap.Config.ARGB_4444)
.into(pieImageView);
13. 总结
主要分析了 OkHttp的源码实现:
1. OkHttp内部是一个门户模式,所有的下发工作都是通过一个门户Dispatcher来进行分发
2. 在网络请求阶段通过责任链模式,链式的调用各个拦截器的intercept方法
重点介绍了2个比较重要的拦截器:Cachelnterceptor和CallServerInterceptor
它们分别用来做请求缓存和执行网络请求操作
3. 在理解源码实现的基础上,对OkHttp的功能进行了一些扩展
实现了网络请求进度的实现
1. Retrofit 与 OkHttp的关系与定位
1.1 核心区别
| 维度 | OkHttp | Retrofit |
|---|---|---|
| 定位 | 底层 HTTP 客户端 | 上层 RESTful 封装框架 |
| API 风格 | 命令式,需要手动构建 Request | 声明式,通过接口注解定义 |
| 数据解析 | 返回原始 ResponseBody,需手动解析 | 内置 Converter,自动完成序列化 |
| 线程切换 | 需手动处理线程切换 | 默认回调主线程(Android 平台) |
| 学习曲线 | 相对平缓 | 略有门槛,但更简洁 |
1.2 为什么配合使用
Retrofit 底层依赖 OkHttp 来执行实际的网络请求。这种分层设计的优势在于:
-
Retrofit 负责将 Java/Kotlin 接口转换为 HTTP 请求
-
OkHttp 负责高效地发送请求并接收响应
-
开发者可以享受 Retrofit 的简洁 API,同时利用 OkHttp 的拦截器、连接池等高级特性
2. Retrofit 核心架构
2.1. Retrofit 架构图
Retrofit 核心组件:
├── 接口定义(声明式API)
├── 动态代理(Proxy)
├── ServiceMethod(服务方法)
├── CallAdapter(调用适配器:RxJava、Coroutine等)
├── Converter(数据转换器:Gson、Moshi等)
├── OkHttpClient(底层网络请求)
└── Call(请求调用)
2.2. Retrofit 执行流程
1. 定义接口 → 2. 创建Retrofit实例 → 3. 创建动态代理
↓
4. 解析注解 → 5. 构建Request → 6. 执行OkHttp调用
↓
7. 转换响应 → 8. 适配调用方式 → 9. 返回结果
3. 最佳实践
3.1 单例模式
OkHttpClient 和 Retrofit 都应该设计为单例,因为每个实例都有自己的连接池和线程池,重复创建会造成资源浪费
// 1. 定义接口
interface ApiService {
@GET("users/{user}/repos")
suspend fun listRepos(@Path("user") user: String): List<Repo>
}
object RetrofitClient {
private const val BASE_URL = "https://api.example.com/"
private const val TIMEOUT = 30L
// 创建OkHttpClient
private val okHttpClient: OkHttpClient by lazy {
OkHttpClient.Builder()
.connectTimeout(TIMEOUT, TimeUnit.SECONDS)
.readTimeout(TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(TIMEOUT, TimeUnit.SECONDS)
.addInterceptor(LoggingInterceptor())
.addInterceptor(AuthInterceptor())
.connectionPool(ConnectionPool(5, 5, TimeUnit.MINUTES))
.cache(Cache(File("cache_dir"), 10 * 1024 * 1024L))
.build()
}
// 2. 创建Retrofit实例
val instance: Retrofit by lazy {
Retrofit.Builder()
.baseUrl(BASE_URL)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create(
GsonBuilder()
.setDateFormat("yyyy-MM-dd HH:mm:ss")
.create()
))
.addConverterFactory(ScalarsConverterFactory.create()) // 支持String
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.build()
}
// 获取API服务
inline fun <reified T> createService(): T {
return instance.create(T::class.java)
}
}
// 3. 创建服务
val apiService = RetrofitClient.createService<ApiService>()
// 4. 使用服务
GlobalScope.launch {
val repos = apiService.listRepos("octocat")
// 处理结果
}
3.2 多 BaseUrl 动态切换
// 1. 定义 BaseUrl 枚举
enum class BaseUrlType(val url: String) {
API_1("https://api1.example.com/"),
API_2("https://api2.example.com/"),
API_3("https://api3.example.com/")
}
// 2. BaseUrl 管理类
object RetrofitManager {
private val retrofitMap = mutableMapOf<BaseUrlType, Retrofit>()
fun getRetrofit(type: BaseUrlType): Retrofit {
return retrofitMap.getOrPut(type) {
Retrofit.Builder()
.baseUrl(type.url)
.client(createOkHttpClient())
.addConverterFactory(GsonConverterFactory.create())
.build()
}
}
fun <T> createService(type: BaseUrlType, serviceClass: Class<T>): T {
return getRetrofit(type).create(serviceClass)
}
}
// 3. 使用
val api1Service = RetrofitManager.createService(BaseUrlType.API_1, ApiService::class.java)
val api2Service = RetrofitManager.createService(BaseUrlType.API_2, ApiService::class.java)
3.3 统一错误处理
// 1. 自定义异常
sealed class ApiException : Exception() {
data class HttpException(
val code: Int,
val message: String? = null
) : ApiException()
data class NetworkException(val cause: Throwable) : ApiException()
data class ParseException(val cause: Throwable) : ApiException()
object UnknownException : ApiException()
}
// 2. 响应包装器
data class ApiResponse<T>(
val data: T? = null,
val error: ApiException? = null
) {
val isSuccess: Boolean get() = error == null
companion object {
fun <T> success(data: T): ApiResponse<T> = ApiResponse(data = data)
fun <T> failure(error: ApiException): ApiResponse<T> = ApiResponse(error = error)
}
}
// 3. 调用扩展函数
suspend fun <T> safeApiCall(apiCall: suspend () -> T): ApiResponse<T> {
return try {
val response = apiCall()
ApiResponse.success(response)
} catch (e: Exception) {
val apiError = when (e) {
is SocketTimeoutException ->
ApiException.NetworkException(e)
is ConnectException ->
ApiException.NetworkException(e)
is HttpException ->
ApiException.HttpException(e.code(), e.message())
is JsonParseException ->
ApiException.ParseException(e)
else ->
ApiException.UnknownException
}
ApiResponse.failure(apiError)
}
}
// 4. 使用
suspend fun loadUsers(): ApiResponse<List<User>> {
return safeApiCall {
apiService.getUsers()
}
}
面试题:
Q:retrofit怎么做到只要写一个接口定义方法就可以请求数据?具体注解和动态代理怎么实现的?
A:动态代理 + 注解解析 + 方法适配器 + 参数处理器
整体架构流程如下:
1. 定义接口 + 注解 → 2. Retrofit.create() → 3. 动态代理创建实现类
↓
4. 方法调用 → 5. ServiceMethod解析 → 6. 构建OkHttpCall → 7. 执行网络请求
1. 定义接口
↓
2. Retrofit.create() 生成动态代理
↓
3. 调用接口方法时,动态代理拦截
↓
4. 解析方法上的注解(@GET、@POST 等)
↓
5.解析参数上的注解(@Path、@Query、@Body 等)
↓
6. 构建 ServiceMethod 对象
↓
7. 通过 CallAdapter 适配返回类型(Call、Observable、suspend 等)
↓
8. 通过 Converter 处理参数和返回值
↓
9. 最终调用 OkHttp 执行请求
动态代理入口:Retrofit.create()
// Retrofit.java
public <T> T create(final Class<T> service) {
// 验证接口合法性
validateServiceInterface(service);
// 核心:使用动态代理创建接口实例
return (T) Proxy.newProxyInstance(
service.getClassLoader(),
new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();
@Override
public Object invoke(Object proxy, Method method, @Nullable Object[] args)
throws Throwable {
// 如果是 Object 的方法(toString、hashCode 等),直接调用
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
// 如果是 default 方法(Java 8+),使用特殊处理
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
// 核心:解析方法注解,构建ServiceMethod,并执行
return loadServiceMethod(method).invoke(args);
}
});
}
关键点:
-
Proxy.newProxyInstance在运行时动态创建一个实现了ApiService接口的代理类 -
当调用
apiService.getUser(123)时,实际上调用的是InvocationHandler.invoke()方法 -
invoke()方法中,根据传入的Method对象,获取对应的ServiceMethod,然后执行
核心:ServiceMethod 的创建
ServiceMethod 是 Retrofit 的核心类,它封装了一个接口方法的完整元数据,包括:
-
HTTP 方法、相对路径
-
请求头
-
参数处理器列表
-
返回值适配器(CallAdapter)
-
响应体转换器(ResponseConverter)
完整执行流程图
调用 apiService.getUser(123)
↓
动态代理 InvocationHandler.invoke()
↓
loadServiceMethod(method) // 从缓存获取或解析
↓
RequestFactory.parseAnnotations()
├── parseMethodAnnotation() // 解析 @GET, @POST 等
├── parseHeaders() // 解析 @Headers
└── parseParameter() // 解析每个参数
└── parseParameterAnnotation() // 解析 @Path, @Query 等
↓
创建 HttpServiceMethod
├── 获取 CallAdapter
├── 获取 ResponseConverter
└── 保存 RequestFactory
↓
HttpServiceMethod.invoke(args)
├── requestFactory.create(args) // 构建 OkHttp Request
│ └── 遍历 ParameterHandler.apply() // 填充路径参数、Query参数等
├── new OkHttpCall<>(request, callFactory, responseConverter)
└── callAdapter.adapt(okHttpCall) // 适配为 Call, Observable, suspend 等
↓
最终调用 OkHttpClient.newCall(request).execute() 或 enqueue()
关键类职责总结
| 类名 | 职责 |
|---|---|
| Retrofit | 核心门面,负责创建动态代理、管理配置(baseUrl、converter、callAdapter) |
| Proxy (动态代理) | 运行时生成接口实现,拦截方法调用 |
| ServiceMethod | 封装一个接口方法的完整元数据,抽象类 |
| HttpServiceMethod | ServiceMethod 的子类,负责实际执行请求 |
| RequestFactory | 解析方法注解和参数注解,构建 Request 对象 |
| ParameterHandler | 处理单个参数(@Path、@Query 等)的策略类 |
| CallAdapter | 适配返回值类型(Call、Observable、suspend 等) |
| Converter | 处理请求体转换和响应体解析 |
| OkHttpCall | 对 OkHttp Call 的封装,实际执行网络请求 |
Q:java的注解机制是怎么做的,怎么实现的
A:它为 Java 语言提供了一种元数据机制,允许开发者在代码中嵌入额外的信息,而这些信息可以在编译时或运行时被读取和处理
注解本质上是一种特殊的接口。当你定义一个注解时,编译器会自动生成一个继承自 java.lang.annotation.Annotation 的接口
// 定义一个注解
public @interface MyAnnotation {
String value() default "";
}
// 上述代码编译后,相当于生成了这样一个接口:
public interface MyAnnotation extends Annotation {
String value();
}
@Retention - 保留策略
指定注解在什么级别保留:
@Retention(RetentionPolicy.SOURCE) // 仅在源码中保留,编译时丢弃
@Retention(RetentionPolicy.CLASS) // 编译时保留在class文件中,运行时不可见(默认)
@Retention(RetentionPolicy.RUNTIME) // 运行时保留,可通过反射获取
// 示例
@Retention(RetentionPolicy.SOURCE)
public @interface SourceAnnotation {} // 仅用于编译期处理
@Retention(RetentionPolicy.RUNTIME)
public @interface RuntimeAnnotation {} // 可用于运行时反射
@Target - 适用目标
指定注解可以修饰哪些程序元素:
@Target(ElementType.TYPE) // 类、接口、枚举
@Target(ElementType.FIELD) // 字段
@Target(ElementType.METHOD) // 方法
@Target(ElementType.PARAMETER) // 参数
@Target(ElementType.CONSTRUCTOR) // 构造函数
@Target(ElementType.LOCAL_VARIABLE)// 局部变量
@Target(ElementType.ANNOTATION_TYPE)// 注解类型
@Target(ElementType.PACKAGE) // 包
@Target(ElementType.TYPE_PARAMETER)// 类型参数 (Java 8)
@Target(ElementType.TYPE_USE) // 类型使用 (Java 8)
// 可以指定多个目标
@Target({ElementType.METHOD, ElementType.FIELD})
public @interface MethodOrFieldAnnotation {}
@Documented - 文档化
使注解信息出现在 Javadoc 中:
@Documented
public @interface DocumentedAnnotation {}
@Inherited - 继承性
允许子类继承父类的注解:
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface InheritedAnnotation {}
@InheritedAnnotation
public class Parent {}
public class Child extends Parent {} // Child 也拥有 InheritedAnnotation
运行时注解的代理机制
当通过反射获取注解时,JVM 返回的是动态代理对象:
// 获取注解的代理对象
MyAnnotation annotation = Test.class.getAnnotation(MyAnnotation.class);
System.out.println(annotation.getClass());
// 输出:class com.sun.proxy.$Proxy1
总结
Java 注解的实现机制:
-
定义阶段:通过 @interface 定义,本质是接口
-
编译阶段:处理元注解,生成字节码
-
运行时:通过动态代理创建实例
-
解析方式:
-
运行时:反射 API
-
编译时:注解处理器
-
-
优化技巧:缓存、懒加载、索引
-
应用场景:配置、验证、依赖注入、代码生成
注解常见问题
Q1: 注解会影响性能吗?
A: 运行时注解通过反射获取,有一定性能开销,但可以通过缓存优化。
Q2: 注解可以继承吗?
A: 默认不继承,使用 @Inherited 元注解可以让子类继承父类的类级别注解。
Q3: 注解可以有构造方法吗?
A: 不可以,注解本质上是一个接口。
Q4: 如何获取注解的默认值?
A: 通过反射获取注解实例,调用方法获取值,如果未设置会返回默认值。
Q5: 注解可以嵌套吗?
A: 可以,注解的属性可以是另一个注解。
Q6: 注解处理器能做什么?
A: 可以在编译时生成代码、验证代码、生成文档等。
更多推荐




所有评论(0)