116人参与 • 2025-04-24 • Android
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 的拦截器链是一个有序的拦截器集合,请求和响应会依次经过每个拦截器。拦截器链的执行顺序是固定的,如下所示:
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);
}
}intercept 方法中,会不断循环处理请求,直到请求成功或者达到最大重试次数。通过判断响应的状态码和异常类型来决定是否进行重试或重定向操作。content-type、content-length、user-agent 等,同时处理请求体的编码和压缩。另外,它还会对响应进行一些处理,比如将响应头中的 content-encoding 信息解析出来,对响应体进行相应的解码操作。intercept 方法中,会根据请求体的情况添加相应的请求头,然后调用 chain.proceed 方法将处理后的请求传递给下一个拦截器,最后对响应进行处理并返回。cache-control 头信息)检查本地缓存中是否存在符合条件的响应。如果存在且缓存有效,则直接返回缓存的响应,避免进行网络请求;如果缓存无效或者不存在,则发起网络请求,并将响应存入缓存。intercept 方法中,会先从缓存中查找匹配的响应,然后根据请求和缓存的情况判断是否可以使用缓存。如果可以使用缓存,则直接返回缓存响应;否则,调用 chain.proceed 方法发起网络请求,并将响应存入缓存。intercept 方法中,会从连接池中获取可用的连接,如果没有可用连接则创建新的连接,然后进行连接的建立和握手操作,最后将连接传递给下一个拦截器。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;
}
}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 类
作用:okhttp 的缓存通过 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 时复用缓存。缓存存储格式
okhttp 将响应以 二进制文件 存储在磁盘,文件名由 url 的哈希值生成,包含两部分:
.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 缓存中如何处理?”
答:okhttp 自动携带 etag 生成 if-none-match 头,服务器返回 304 时,复用本地缓存响应体,仅更新头信息(减少流量)。
“okhttp 缓存和浏览器缓存的区别?”
答:核心逻辑一致(基于 http 头),但 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)一致。1. 默认使用(无需额外配置)
okhttpclient 默认启用连接池,无需手动设置,同一okhttpclient实例的所有请求共享同一个连接池:
okhttpclient client = new okhttpclient.builder()
.connecttimeout(10, timeunit.seconds)
.build(); // 内部使用默认的connectionpool2. 自定义连接池配置(可选)
若需调整默认参数(如增大空闲连接数或存活时间),可通过connectionpool()方法设置:
okhttpclient client = new okhttpclient.builder()
.connectionpool(new connectionpool(
10, // 最大空闲连接数设为10
10, timeunit.minutes // 空闲连接存活时间设为10分钟
))
.build();3. 连接池的生命周期
cleanuprunnable)定时检查(每隔 5 秒),清理超时的空闲连接。client.connectionpool().evictall(); // 清除所有连接
连接获取流程
当发起请求时,okhttp 先从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;
}
}
}
}
};1. 为什么需要连接池复用?相比 httpurlconnection 有什么优势?
原因:避免重复建立 tcp 连接(三次握手)和 tls 握手(https 场景),减少延迟和资源消耗。
优势:
httpurlconnection.setinstancefollowredirects(true),且管理复杂);connectionpool自动管理连接生命周期,线程安全,开箱即用。2. 如何判断两个请求是否可以复用同一个连接?
必须满足:
host和port相同;3. 连接池中的连接会一直存在吗?如何避免内存泄漏?
不会:
maxidleconnections的空闲连接会被清理;keepaliveduration的空闲连接会被关闭;connectionpool.evictall()释放所有连接。最佳实践:使用单例okhttpclient(避免创建多个实例导致多个连接池),并合理设置maxidleconnections(通常默认值即可)。
4. 连接池和缓存机制(cacheinterceptor)的关系是什么?
单例模式:全局共享一个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拦截器和缓存的文章就介绍到这了,更多相关android okhttp拦截器内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
您想发表意见!!点此发布评论
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论