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 会按顺序添加一系列拦截器 :

  1. RetryAndFollowUpInterceptor:负责处理失败重试和重定向。如果请求失败,它会进行重试;如果收到重定向响应,它会创建新的请求并重新走一遍流程 。

  2. BridgeInterceptor:桥接拦截器。它负责将用户构建的 Request 转换为能够进行网络传输的请求,比如添加 Content-LengthHostUser-Agent 等头部信息,并处理 GZIP 解压 。

  3. CacheInterceptor:缓存拦截器。它负责读取缓存和更新缓存。如果缓存命中且未过期,直接返回缓存数据,不再发起网络请求 。

  4. ConnectInterceptor:连接拦截器。它负责与服务器建立连接。它会在连接池中寻找可用的连接,如果没有则创建一个新的连接(包括 TCP 握手和 TLS 握手)。

  5. 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

关键逻辑:

  1. 链按顺序执行拦截器

  2. 每个拦截器调用 chain.proceed(request)传递给下一个

  3. 最后一个拦截器(CallServerInterceptor)实际发起网络请求

  4. 响应逆序返回给各个拦截器

总结

正确顺序:

阶段

拦截器类型

执行顺序

请求阶段

应用拦截器

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                             // 返回响应

  1. 入口:Call 的创建OkHttpClient.newCall(request) 实际上创建了一个 RealCall 对象。

  2. 执行:execute() 或 enqueue()

    • 如果是同步,直接调用 getResponseWithInterceptorChain()

    • 如果是异步,RealCall.enqueue() 会将请求交给 Dispatcher(分发器)处理。Dispatcher 内部维护了线程池和等待队列,负责并发控制(默认最大并发请求 64 个,单个主机最大 5 个)。

  3. 核心:getResponseWithInterceptorChain():这是最核心的方法。它创建一个拦截器链,并将上述提到的所有拦截器添加到列表中,然后启动链条,让请求依次通过每个拦截器。

  4. 递归调用:RealInterceptorChain.proceed():链条的运作机制是递归的。每个拦截器在 intercept() 方法中处理完自己的逻辑后,会调用 chain.proceed(request),将请求传递给下一个拦截器,直到最后一个拦截器执行真正的 I/O 操作并返回 ResponseResponse 再沿着链条逆向传递回来,被每个拦截器处理(例如 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个比较重要的拦截器:CachelnterceptorCallServerInterceptor
    它们分别用来做请求缓存执行网络请求操作
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 注解的实现机制:

  1. 定义阶段:通过 @interface 定义,本质是接口

  2. 编译阶段:处理元注解,生成字节码

  3. 运行时:通过动态代理创建实例

  4. 解析方式

    • 运行时:反射 API

    • 编译时:注解处理器

  5. 优化技巧:缓存、懒加载、索引

  6. 应用场景:配置、验证、依赖注入、代码生成

注解常见问题

Q1: 注解会影响性能吗?

A:​ 运行时注解通过反射获取,有一定性能开销,但可以通过缓存优化。

Q2: 注解可以继承吗?

A:​ 默认不继承,使用 @Inherited 元注解可以让子类继承父类的类级别注解。

Q3: 注解可以有构造方法吗?

A:不可以,注解本质上是一个接口

Q4: 如何获取注解的默认值?

A:​ 通过反射获取注解实例,调用方法获取值,如果未设置会返回默认值。

Q5: 注解可以嵌套吗?

A:​ 可以,注解的属性可以是另一个注解。

Q6: 注解处理器能做什么?

A:​ 可以在编译时生成代码、验证代码、生成文档等。


Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐