Interceptor
接口是自定义拦截器的基础,它仅包含一个抽象方法 intercept
。以下是对该方法参数和返回值的详细解释:
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;
public class CustomInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
// chain 包含了当前请求的所有信息以及后续拦截器的处理逻辑
Request originalRequest = chain.request();
// 可以对原始请求进行修改,例如添加请求头、修改请求方法等
Request modifiedRequest = originalRequest.newBuilder()
.header("Custom-Header", "Custom-Value")
.build();
// proceed 方法会将修改后的请求传递给下一个拦截器,并返回响应
Response response = chain.proceed(modifiedRequest);
// 可以对响应进行处理,例如添加自定义响应头、解析响应体等
return response.newBuilder()
.header("Custom-Response-Header", "Custom-Response-Value")
.build();
}
}
Chain
参数:Chain
是一个接口,它代表了整个拦截器链。chain.request()
方法可以获取当前的请求对象;chain.proceed(request)
方法会将请求传递给下一个拦截器,并返回响应。Response
返回值:intercept
方法必须返回一个 Response
对象,这个对象可以是原始响应,也可以是经过修改后的响应。OkHttp 的拦截器链是一个有序的拦截器集合,请求和响应会依次经过每个拦截器。拦截器链的执行顺序是固定的,如下所示:
client.interceptors()
)import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;
public class CustomHeaderInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
Request newRequest = originalRequest.newBuilder()
.header("Custom-Header", "Custom-Value")
.build();
return chain.proceed(newRequest);
}
}
RetryAndFollowUpInterceptor
)intercept
方法中,会不断循环处理请求,直到请求成功或者达到最大重试次数。通过判断响应的状态码和异常类型来决定是否进行重试或重定向操作。BridgeInterceptor
)Content-Type
、Content-Length
、User-Agent
等,同时处理请求体的编码和压缩。另外,它还会对响应进行一些处理,比如将响应头中的 Content-Encoding
信息解析出来,对响应体进行相应的解码操作。intercept
方法中,会根据请求体的情况添加相应的请求头,然后调用 chain.proceed
方法将处理后的请求传递给下一个拦截器,最后对响应进行处理并返回。CacheInterceptor
)Cache-Control
头信息)检查本地缓存中是否存在符合条件的响应。如果存在且缓存有效,则直接返回缓存的响应,避免进行网络请求;如果缓存无效或者不存在,则发起网络请求,并将响应存入缓存。intercept
方法中,会先从缓存中查找匹配的响应,然后根据请求和缓存的情况判断是否可以使用缓存。如果可以使用缓存,则直接返回缓存响应;否则,调用 chain.proceed
方法发起网络请求,并将响应存入缓存。ConnectInterceptor
)intercept
方法中,会从连接池中获取可用的连接,如果没有可用连接则创建新的连接,然后进行连接的建立和握手操作,最后将连接传递给下一个拦截器。client.networkInterceptors()
)import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;
public class NetworkLoggingInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
long t1 = System.nanoTime();
System.out.println(String.format("Sending request %s on %s%n%s",
request.url(), chain.connection(), request.headers()));
Response response = chain.proceed(request);
long t2 = System.nanoTime();
System.out.println(String.format("Received response for %s in %.1fms%n%s",
response.request().url(), (t2 - t1) / 1e6d, response.headers()));
return response;
}
}
CallServerInterceptor
)intercept
方法中,会将请求体写入连接的输出流,发送请求头,然后从连接的输入流中读取响应头和响应体,最后返回响应对象。OkHttpClient.Builder().addInterceptor(Interceptor interceptor)
方法添加。import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;
public class ApplicationInterceptorExample {
public static void main(String[] args) throws IOException {
CustomInterceptor customInterceptor = new CustomInterceptor();
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(customInterceptor)
.build();
Request request = new Request.Builder()
.url("https://example.com")
.build();
Response response = client.newCall(request).execute();
System.out.println(response.body().string());
}
}
OkHttpClient.Builder().addNetworkInterceptor(Interceptor interceptor)
方法添加。import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;
public class NetworkInterceptorExample {
public static void main(String[] args) throws IOException {
CustomInterceptor customInterceptor = new CustomInterceptor();
OkHttpClient client = new OkHttpClient.Builder()
.addNetworkInterceptor(customInterceptor)
.build();
Request request = new Request.Builder()
.url("https://example.com")
.build();
Response response = client.newCall(request).execute();
System.out.println(response.body().string());
}
}
可以创建一个拦截器来动态控制缓存策略,例如根据网络状态或用户设置来决定是否使用缓存。
import okhttp3.*;
import java.io.IOException;
public class CacheControlInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
if (isNetworkAvailable()) {
// 网络可用时,设置缓存策略为最多缓存 60 秒
request = request.newBuilder()
.header("Cache-Control", "max-age=60")
.build();
} else {
// 网络不可用时,强制使用缓存
request = request.newBuilder()
.header("Cache-Control", "only-if-cached")
.build();
}
return chain.proceed(request);
}
private boolean isNetworkAvailable() {
// 实现网络状态检查逻辑
return true;
}
}
可以创建一个拦截器来处理请求超时的情况,当请求超时时,自动重试一定次数。
import okhttp3.*;
import java.io.IOException;
public class RetryInterceptor implements Interceptor {
private static final int MAX_RETRIES = 3;
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response = null;
IOException exception = null;
for (int i = 0; i < MAX_RETRIES; i++) {
try {
response = chain.proceed(request);
if (response.isSuccessful()) {
break;
}
} catch (IOException e) {
exception = e;
}
}
if (response == null) {
throw exception;
}
return response;
}
}
如何保证OKHttp 拦截器链中每个拦截器能按预定顺序执行
答:OKHttp 的拦截器顺序就像一场 “接力赛”:
chain.proceed()
是接力棒,确保每个拦截器按顺序处理请求,响应按逆序回流,环环相扣,不会混乱。intercept
方法调用每个拦截器都实现了 Interceptor
接口,该接口有一个 intercept
方法。在 intercept
方法中,需要调用传入的 Chain
对象的 proceed
方法,将请求传递给下一个拦截器。例如 BridgeInterceptor
的 intercept
方法:
@Override public Response intercept(Chain chain) throws IOException {
Request userRequest = chain.request();
Request.Builder requestBuilder = userRequest.newBuilder();
// 处理请求头
RequestBody body = userRequest.body();
if (body != null) {
MediaType contentType = body.contentType();
if (contentType != null) {
requestBuilder.header("Content-Type", contentType.toString());
}
long contentLength = body.contentLength();
if (contentLength != -1) {
requestBuilder.header("Content-Length", Long.toString(contentLength));
requestBuilder.removeHeader("Transfer-Encoding");
} else {
requestBuilder.header("Transfer-Encoding", "chunked");
requestBuilder.removeHeader("Content-Length");
}
}
Request networkRequest = requestBuilder.build();
// 调用 chain.proceed 方法将请求传递给下一个拦截器
Response networkResponse = chain.proceed(networkRequest);
// 处理响应
Response.Builder responseBuilder = networkResponse.newBuilder()
.request(userRequest);
return responseBuilder.build();
}
在 intercept
方法中调用 chain.proceed
方法,就会触发下一个拦截器的执行,进而保证拦截器链按顺序执行。
———————————————————————————————————————————
Cache
类
Cache
类实现,基于磁盘存储(默认无内存缓存,需手动实现)。File cacheDir = new File(context.getCacheDir(), "okhttp_cache");
OkHttpClient client = new OkHttpClient.Builder()
.cache(new Cache(cacheDir, 10 * 1024 * 1024)) // 10MB 缓存大小
.build();
getCacheDir()
),避免权限问题;缓存大小需根据业务场景合理设置,过大浪费存储,过小导致缓存命中率低。CacheInterceptor
拦截器
RetryAndFollowUpInterceptor
和 ConnectInterceptor
之间)。OkHttp 支持 HTTP 标准缓存策略(基于 Cache-Control
头)和 自定义策略,通过 Request
的 CacheControl
对象配置,常见策略:
强制缓存(不与服务器交互)
CacheControl.FORCE_CACHE
:优先使用缓存,无缓存时抛异常(需配合 max-stale
等参数)。缓存优先(无有效缓存时请求网络)
CacheControl.cacheControl(CacheControl.Builder() .maxStale(7, TimeUnit.DAYS) // 允许缓存过期 7 天 .build())
max-stale
,直接返回;否则请求网络,响应写入缓存。网络优先(忽略缓存,仅存储响应)
CacheControl.FORCE_NETWORK
:直接请求网络,响应结果写入缓存(适用于实时数据)。协商缓存(与服务器验证缓存有效性)
ETag
/If-None-Match
或 Last-Modified
/If-Modified-Since
头,服务器返回 304 Not Modified
时复用缓存。缓存存储格式
.headers
):存储 Cache-Control
、ETag
等元信息。关键 HTTP 头字段
Cache-Control
:
max-age
:缓存有效期(秒),优先级高于 Expires
。no-cache
:需走协商缓存(验证有效性),no-store
:禁止缓存。ETag
/Last-Modified
:协商缓存的核心字段,OkHttp 自动处理 If-None-Match
和 If-Modified-Since
头。缓存读取(CacheInterceptor
前半段)
Cache-Control
判定是否有效:
max-stale
:返回缓存,同时异步更新网络数据。网络请求与缓存写入(CacheInterceptor
后半段)
Cache-Control
决定是否写入缓存(如 max-age > 0
)。Cache
类配置,持久化存储,适合大文件或需离线访问的场景。LruCache
),OkHttp 不默认支持,用于加速热数据访问,减少磁盘 IO。Interceptor
手动实现,存储已处理的 Response
对象。client.cache().delete(); // 清除所有缓存
client.cache().evictAll(); // 同上(API 差异)
CacheControl.noCache()
,强制忽略缓存,走网络请求。“OkHttp 缓存策略有哪些?如何实现缓存优先?”
FORCE_CACHE
(强制读缓存)、FORCE_NETWORK
(强制网络)、协商缓存(304)等;缓存优先可通过 maxStale
允许过期缓存,配合网络请求更新。“304 状态码在 OkHttp 缓存中如何处理?”
ETag
生成 If-None-Match
头,服务器返回 304 时,复用本地缓存响应体,仅更新头信息(减少流量)。“OkHttp 缓存和浏览器缓存的区别?”
Cache
实例,且默认无内存缓存;浏览器缓存由浏览器自动管理。“缓存拦截器的作用是什么?在拦截器链中的位置?”
OkHttp 缓存机制通过 拦截器链 和 HTTP 标准头 实现高效的网络请求优化,核心在于合理配置 CacheControl
策略、利用协商缓存减少服务器压力,并结合磁盘 / 内存缓存提升性能。--
_____________________________________________________________________________
OkHttp 的连接池复用是优化网络请求性能的重要手段,其核心是通过ConnectionPool
管理底层 TCP 连接,避免重复建立连接的开销。
目标
复用相同 URL、相同协议(HTTP/HTTPS)的连接,减少 TCP 三次握手、TLS 握手的耗时,提升请求速度。
核心类:ConnectionPool
OkHttpClient
默认创建): // OkHttpClient源码中的默认连接池
private static final ConnectionPool DEFAULT_CONNECTION_POOL = new ConnectionPool(
5, // 最大空闲连接数(默认5个)
5, TimeUnit.MINUTES // 空闲连接存活时间(默认5分钟)
);
maxIdleConnections
:最大空闲连接数,超过则清理最旧的连接。keepAliveDuration
:空闲连接在池中的最长存活时间,超时则关闭。连接复用条件
host
和port
相同,且协议(HTTP/HTTPS)一致。OkHttpClient 默认启用连接池,无需手动设置,同一OkHttpClient
实例的所有请求共享同一个连接池:
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.build(); // 内部使用默认的ConnectionPool
若需调整默认参数(如增大空闲连接数或存活时间),可通过connectionPool()
方法设置:
OkHttpClient client = new OkHttpClient.Builder()
.connectionPool(new ConnectionPool(
10, // 最大空闲连接数设为10
10, TimeUnit.MINUTES // 空闲连接存活时间设为10分钟
))
.build();
CleanupRunnable
)定时检查(每隔 5 秒),清理超时的空闲连接。client.connectionPool().evictAll(); // 清除所有连接
连接获取流程
ConnectionPool
中查找可用的空闲连接: // RealConnectionPool.java 查找连接的核心逻辑
RealConnection get(Address address, StreamAllocation streamAllocation) {
// 遍历连接池中的连接,寻找匹配address且空闲的连接
for (RealConnection connection : connections) {
if (connection.isEligible(address, streamAllocation)) {
streamAllocation.acquire(connection);
return connection;
}
}
return null; // 无可用连接,新建连接
}
isEligible
方法判断连接是否符合复用条件(host、port、协议一致,且未达最大请求数)。连接释放与空闲标记
// RealConnection.java 释放连接的逻辑
void release(StreamAllocation streamAllocation) {
if (streamAllocation == null) return;
streamAllocation.release();
if (allocationCount > 0 || noNewStreams) {
return; // 连接仍在使用中
}
// 连接变为空闲,加入连接池的空闲队列
connectionPool.put(this);
}
清理机制
ConnectionPool
通过CleanupRunnable
线程定时执行cleanup()
方法,移除超时或超出最大空闲数的连接: // ConnectionPool.java 清理逻辑
private final Runnable cleanupRunnable = () -> {
while (true) {
long waitNanos = cleanup(System.nanoTime()); // 执行清理,返回下次等待时间
if (waitNanos == -1) return; // 无需要清理的连接,退出
if (waitNanos > 0) {
synchronized (this) {
try {
wait(waitNanos / 1000000, (int) (waitNanos % 1000000));
} catch (InterruptedException e) {
return;
}
}
}
}
};
HttpURLConnection.setInstanceFollowRedirects(true)
,且管理复杂);ConnectionPool
自动管理连接生命周期,线程安全,开箱即用。host
和port
相同;maxIdleConnections
的空闲连接会被清理;keepAliveDuration
的空闲连接会被关闭;connectionPool.evictAll()
释放所有连接。OkHttpClient
(避免创建多个实例导致多个连接池),并合理设置maxIdleConnections
(通常默认值即可)。OkHttpClient
实例,避免重复创建连接池: public class OkHttpSingleton {
private static OkHttpClient client;
public static OkHttpClient getInstance() {
if (client == null) {
synchronized (OkHttpSingleton.class) {
if (client == null) {
client = new OkHttpClient.Builder()
.connectionPool(new ConnectionPool(5, 5, TimeUnit.MINUTES))
.build();
}
}
}
return client;
}
}
EventListener
监听连接池事件(如连接创建、复用、释放),排查性能问题。OkHttp 的连接池复用通过ConnectionPool
自动管理空闲连接,显著提升网络请求效率。使用时无需手动干预,只需合理配置参数(或使用默认值),并遵循单例模式共享OkHttpClient
实例即可。
核心回答点:
ConnectionPool
管理连接,默认维护 5 个空闲连接(maxIdleConnections
),存活时间 5 分钟(keepAliveDuration
)。cleanupRunnable
)定期清理过期连接。Connection: Keep-Alive
,OkHttp 实现更高效,支持自动管理连接生命周期,降低资源消耗。核心回答点:
CacheInterceptor
管理):存储响应数据,快速响应重复请求,减少 CPU 和内存开销。Cache
类,需手动创建):持久化存储,应对 APP 重启或长时间未请求的场景。CacheControl
头配置,如 FORCE_CACHE
(优先读缓存)、FORCE_NETWORK
(强制走网络)、CACHE_ELSE_NETWORK
(缓存失效后走网络)。304 Not Modified
)和 协商缓存(服务端验证缓存有效性),OkHttp 内置拦截器自动处理缓存响应码。Cache
对象并设置大小(如 new Cache(cacheDir, 10 * 1024 * 1024)
),通过 OkHttpClient.Builder().cache(cache)
绑定。核心回答点:
Interceptor
接口的 intercept
方法,调用 chain.proceed(request)
传递请求。核心回答点:
Response
。Dispatcher
调度到线程池(默认 ExecutorService
),回调 Callback
在子线程,需手动通过 Handler
切回主线程。Dispatcher
控制最大并发请求数(默认 64 个,同一主机 5 个),异步请求通过 AsyncCall
封装,放入队列或直接执行。runOnUiThread
)。核心回答点:
核心回答点:
maxIdleConnections
和 keepAliveDuration
(如高频接口增大连接数)。CacheControl.maxAge
),减少无效网络请求。CertificatePinner
固定证书,避免 SSL 握手耗时;启用 HTTP/2(需服务端支持)。Dispatcher.setMaxRequests
和 setMaxRequestsPerHost
限制并发,避免资源耗尽。基础篇
:Android第六次面试总结(okhttp原理篇)-CSDN博客https://blog.csdn.net/2301_80329517/article/details/146487074?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522a0804a8e52f2c3d0520212f8ba7ad17a%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=a0804a8e52f2c3d0520212f8ba7ad17a&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-1-146487074-null-null.nonecase&utm_term=okHttp&spm=1018.2226.3001.4450