Android学习总结之OKHttp拦截器和缓存
深入理解 OkHttp 拦截器
1. 拦截器接口详解
Interceptor
接口是自定义拦截器的基础,它仅包含一个抽象方法 intercept
。以下是对该方法参数和返回值的详细解释:
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;public class CustomInterceptor implements Interceptor {@Overridepublic 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
对象,这个对象可以是原始响应,也可以是经过修改后的响应。
2.拦截器链的详细执行流程
整体流程
OkHttp 的拦截器链是一个有序的拦截器集合,请求和响应会依次经过每个拦截器。拦截器链的执行顺序是固定的,如下所示:
1. 用户自定义拦截器(client.interceptors()
)
- 位置:拦截器链的最前端。
- 作用:这是开发者可以自定义添加的拦截器,开发者可以在这个拦截器中实现一些通用的业务逻辑,比如统一添加请求头、日志记录、请求参数加密等操作。由于它处于拦截器链的最前端,所以可以对原始的请求进行最早的处理,并且能获取到最终的响应,方便进行日志记录等操作。
- 示例代码:
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;public class CustomHeaderInterceptor implements Interceptor {@Overridepublic Response intercept(Chain chain) throws IOException {Request originalRequest = chain.request();Request newRequest = originalRequest.newBuilder().header("Custom-Header", "Custom-Value").build();return chain.proceed(newRequest);}
}
2. 重试和重定向拦截器(RetryAndFollowUpInterceptor
)
- 位置:紧跟用户自定义拦截器之后。
- 作用:负责处理请求的重试和重定向逻辑。当请求过程中出现网络错误(如连接超时、DNS 解析失败等)时,该拦截器会根据配置的重试策略进行重试;当服务器返回重定向响应(如 301、302 状态码)时,会自动处理重定向请求,重新发起新的请求到重定向的地址。
- 源码分析:在
intercept
方法中,会不断循环处理请求,直到请求成功或者达到最大重试次数。通过判断响应的状态码和异常类型来决定是否进行重试或重定向操作。
3. 桥接拦截器(BridgeInterceptor
)
- 位置:在重试和重定向拦截器之后。
- 作用:主要负责将用户的请求转换为符合网络传输规范的请求。它会添加一些必要的请求头,如
Content-Type
、Content-Length
、User-Agent
等,同时处理请求体的编码和压缩。另外,它还会对响应进行一些处理,比如将响应头中的Content-Encoding
信息解析出来,对响应体进行相应的解码操作。 - 源码分析:在
intercept
方法中,会根据请求体的情况添加相应的请求头,然后调用chain.proceed
方法将处理后的请求传递给下一个拦截器,最后对响应进行处理并返回。
4. 缓存拦截器(CacheInterceptor
)
- 位置:在桥接拦截器之后。
- 作用:负责处理请求的缓存逻辑。它会根据请求的缓存策略(如
Cache-Control
头信息)检查本地缓存中是否存在符合条件的响应。如果存在且缓存有效,则直接返回缓存的响应,避免进行网络请求;如果缓存无效或者不存在,则发起网络请求,并将响应存入缓存。 - 源码分析:在
intercept
方法中,会先从缓存中查找匹配的响应,然后根据请求和缓存的情况判断是否可以使用缓存。如果可以使用缓存,则直接返回缓存响应;否则,调用chain.proceed
方法发起网络请求,并将响应存入缓存。
5. 连接拦截器(ConnectInterceptor
)
- 位置:在缓存拦截器之后。
- 作用:负责建立与服务器的连接。它会根据请求的 URL 和配置,选择合适的连接(如 HTTP/1.1 或 HTTP/2 连接),并进行 TCP 握手和 TLS 协商(如果是 HTTPS 请求)。同时,它会管理连接池,复用已经建立的连接,减少连接建立的开销。
- 源码分析:在
intercept
方法中,会从连接池中获取可用的连接,如果没有可用连接则创建新的连接,然后进行连接的建立和握手操作,最后将连接传递给下一个拦截器。
6. 用户自定义网络拦截器(client.networkInterceptors()
)
- 位置:在连接拦截器之后,仅在进行网络请求时会执行。
- 作用:与用户自定义拦截器类似,但它更侧重于对网络请求和响应进行处理。由于它在连接建立之后执行,所以可以获取到实际的网络连接信息,并且可以对网络请求和响应进行更底层的修改,比如修改请求的字节流、监控网络流量等。
- 示例代码:
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;public class NetworkLoggingInterceptor implements Interceptor {@Overridepublic 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;}
}
7. 服务器调用拦截器(CallServerInterceptor
)
- 位置:拦截器链的最后一个拦截器。
- 作用:负责向服务器发送请求并接收服务器的响应。它会将请求数据写入网络连接,然后读取服务器返回的响应数据,包括响应头和响应体。
- 源码分析:在
intercept
方法中,会将请求体写入连接的输出流,发送请求头,然后从连接的输入流中读取响应头和响应体,最后返回响应对象。
3.应用拦截器和网络拦截器的区别
应用拦截器
- 添加方式:通过
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());}
}
4. 拦截器的高级应用场景
缓存控制拦截器
可以创建一个拦截器来动态控制缓存策略,例如根据网络状态或用户设置来决定是否使用缓存。
import okhttp3.*;
import java.io.IOException;public class CacheControlInterceptor implements Interceptor {@Overridepublic 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;@Overridepublic 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
方法,就会触发下一个拦截器的执行,进而保证拦截器链按顺序执行。
———————————————————————————————————————————
OkHttp 缓存机制详解(结合 Android 面试高频考点)
一、缓存核心组件与配置
-
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()
),避免权限问题;缓存大小需根据业务场景合理设置,过大浪费存储,过小导致缓存命中率低。
- 作用:OkHttp 的缓存通过
-
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
时复用缓存。 - 面试点:区分强制缓存(200 状态码,直接读缓存)和协商缓存(304 状态码,需服务器验证)。
- 利用
三、缓存存储结构与 HTTP 头解析
-
缓存存储格式
- OkHttp 将响应以 二进制文件 存储在磁盘,文件名由 URL 的哈希值生成,包含两部分:
- 响应头文件(
.headers
):存储Cache-Control
、ETag
等元信息。 - 响应体文件(无扩展名):存储实际数据。
- 响应头文件(
- OkHttp 将响应以 二进制文件 存储在磁盘,文件名由 URL 的哈希值生成,包含两部分:
-
关键 HTTP 头字段
Cache-Control
:max-age
:缓存有效期(秒),优先级高于Expires
。no-cache
:需走协商缓存(验证有效性),no-store
:禁止缓存。
ETag
/Last-Modified
:协商缓存的核心字段,OkHttp 自动处理If-None-Match
和If-Modified-Since
头。
四、缓存流程与拦截器逻辑
-
缓存读取(
CacheInterceptor
前半段)- 从缓存中查找与请求匹配的响应(根据 URL、方法、头信息)。
- 若存在缓存,根据
Cache-Control
判定是否有效:- 有效:直接返回缓存(跳过网络请求)。
- 过期但允许
max-stale
:返回缓存,同时异步更新网络数据。
-
网络请求与缓存写入(
CacheInterceptor
后半段)- 网络响应返回后,根据
Cache-Control
决定是否写入缓存(如max-age > 0
)。 - 写入前检查响应状态码(仅 200 OK 和 304 会被缓存),并提取必要的头信息用于后续验证。
- 网络响应返回后,根据
五、内存缓存与磁盘缓存(面试易混点)
- 磁盘缓存:OkHttp 内置,通过
Cache
类配置,持久化存储,适合大文件或需离线访问的场景。 - 内存缓存:需手动实现(如使用
LruCache
),OkHttp 不默认支持,用于加速热数据访问,减少磁盘 IO。 - 面试问法:“OkHttp 有没有内存缓存?如何实现?” 答:默认只有磁盘缓存,内存缓存需结合
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 自动携带
-
“OkHttp 缓存和浏览器缓存的区别?”
- 答:核心逻辑一致(基于 HTTP 头),但 OkHttp 需手动配置
Cache
实例,且默认无内存缓存;浏览器缓存由浏览器自动管理。
- 答:核心逻辑一致(基于 HTTP 头),但 OkHttp 需手动配置
-
“缓存拦截器的作用是什么?在拦截器链中的位置?”
- 答:负责缓存的读取和写入,位于拦截器链的中间位置(处理完重试、桥接,未处理连接和网络请求)。
总结
OkHttp 缓存机制通过 拦截器链 和 HTTP 标准头 实现高效的网络请求优化,核心在于合理配置 CacheControl
策略、利用协商缓存减少服务器压力,并结合磁盘 / 内存缓存提升性能。--
_____________________________________________________________________________
OkHttp 的连接池复用是优化网络请求性能的重要手段,其核心是通过ConnectionPool
管理底层 TCP 连接,避免重复建立连接的开销。
一、OkHttp 连接池复用的核心原理
-
目标
复用相同 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
:空闲连接在池中的最长存活时间,超时则关闭。
-
连接复用条件
- 请求的 URL 的
host
和port
相同,且协议(HTTP/HTTPS)一致。 - 连接处于 “空闲状态”(即当前无请求正在使用,但未超时)。
- 请求的 URL 的
二、如何使用连接池复用?
1. 默认使用(无需额外配置)
OkHttpClient 默认启用连接池,无需手动设置,同一OkHttpClient
实例的所有请求共享同一个连接池:
OkHttpClient client = new OkHttpClient.Builder().connectTimeout(10, TimeUnit.SECONDS).build(); // 内部使用默认的ConnectionPool
2. 自定义连接池配置(可选)
若需调整默认参数(如增大空闲连接数或存活时间),可通过connectionPool()
方法设置:
OkHttpClient client = new OkHttpClient.Builder().connectionPool(new ConnectionPool(10, // 最大空闲连接数设为1010, TimeUnit.MINUTES // 空闲连接存活时间设为10分钟)).build();
3. 连接池的生命周期
- 自动清理:OkHttp 通过后台线程(
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、协议一致,且未达最大请求数)。
- 当发起请求时,OkHttp 先从
-
连接释放与空闲标记
- 请求完成后,连接不会立即关闭,而是标记为 “空闲” 并放回连接池:
// 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 默认不支持连接复用(需手动配置
HttpURLConnection.setInstanceFollowRedirects(true)
,且管理复杂); - OkHttp 的
ConnectionPool
自动管理连接生命周期,线程安全,开箱即用。
- HTTPURLConnection 默认不支持连接复用(需手动配置
2. 如何判断两个请求是否可以复用同一个连接?
- 必须满足:
- URL 的
host
和port
相同; - 协议相同(均为 HTTP 或均为 HTTPS);
- 连接处于空闲状态(未被占用且未超时)。
- URL 的
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;} }
- 结合 HTTPS 优化:复用连接时,TLS 握手仅在首次建立连接时执行,后续请求直接复用已建立的加密通道。
- 监控与调试:通过
EventListener
监听连接池事件(如连接创建、复用、释放),排查性能问题。
总结
OkHttp 的连接池复用通过ConnectionPool
自动管理空闲连接,显著提升网络请求效率。使用时无需手动干预,只需合理配置参数(或使用默认值),并遵循单例模式共享OkHttpClient
实例即可。
扩展追问:
1. OkHttp 如何实现连接池复用?(高频题)
核心回答点:
- ConnectionPool 组件:OkHttp 通过
ConnectionPool
管理连接,默认维护 5 个空闲连接(maxIdleConnections
),存活时间 5 分钟(keepAliveDuration
)。 - 复用逻辑:新请求优先从连接池中查找匹配的空闲连接(同主机、端口、协议),避免重复创建 TCP 连接和 TLS 握手,减少延迟。
- 连接回收:请求完成后,连接不会立即关闭,而是放入池中等待复用;若空闲时间超过阈值或连接数超过上限,通过后台线程(
cleanupRunnable
)定期清理过期连接。 - 面试加分项:对比 HTTP/1.1 的
Connection: Keep-Alive
,OkHttp 实现更高效,支持自动管理连接生命周期,降低资源消耗。
2. 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)
绑定。
3. 拦截器链(Interceptor Chain)的作用是什么?自定义拦截器如何实现?(原理题)
核心回答点:
- 责任链模式:OkHttp 通过拦截器链处理请求和响应,包括 重试与重定向、桥接(添加请求头 / 处理响应体)、缓存、连接建立、网络请求 等内置拦截器。
- 执行顺序:用户自定义拦截器 → 内置重试拦截器 → 桥接拦截器 → 缓存拦截器 → 连接拦截器 → 网络拦截器 → 调用服务器拦截器。
- 自定义场景:用于添加公共请求头(如 Token)、日志打印、响应数据解析 / 修改,通过实现
Interceptor
接口的intercept
方法,调用chain.proceed(request)
传递请求。 - 面试关键:强调拦截器的 “中间件” 特性,可在不修改核心代码的前提下扩展功能,符合开闭原则。
4. 同步请求(execute)和异步请求(enqueue)的区别是什么?如何实现线程切换?(线程题)
核心回答点:
- 执行方式:
- 同步:阻塞当前线程,在主线程调用会导致 ANR,需在子线程执行,直接返回
Response
。 - 异步:通过
Dispatcher
调度到线程池(默认ExecutorService
),回调Callback
在子线程,需手动通过Handler
切回主线程。
- 同步:阻塞当前线程,在主线程调用会导致 ANR,需在子线程执行,直接返回
- 线程管理:
Dispatcher
控制最大并发请求数(默认 64 个,同一主机 5 个),异步请求通过AsyncCall
封装,放入队列或直接执行。 - 面试陷阱:避免混淆 “异步回调是否在主线程”,OkHttp 不负责线程切换,需开发者自行处理(如
runOnUiThread
)。
5. OkHttp 相比 Volley 或 HttpURLConnection 的优势是什么?(对比题)
核心回答点:
- 性能优化:连接池复用、缓存策略、SPDY/HTTP/2 支持(减少 TCP 连接数),网络请求效率更高。
- 扩展性:拦截器机制灵活,方便添加日志、重试、加密等功能,而 Volley 更适合小量短连接请求。
- 稳定性:内置重试机制(自动处理连接超时、重定向),支持流式响应处理(大文件下载),适合复杂网络场景。
- 面试加分:结合实际项目,说明 OkHttp 在处理高并发、大文件、复杂网络环境下的优势。
6. 如何优化 OkHttp 的网络请求性能?(实战题)
核心回答点:
- 连接池调优:根据业务场景调整
maxIdleConnections
和keepAliveDuration
(如高频接口增大连接数)。 - 缓存策略:合理设置缓存有效期(
CacheControl.maxAge
),减少无效网络请求。 - HTTPS 优化:使用
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
相关文章:
Android学习总结之OKHttp拦截器和缓存
深入理解 OkHttp 拦截器 1. 拦截器接口详解 Interceptor 接口是自定义拦截器的基础,它仅包含一个抽象方法 intercept。以下是对该方法参数和返回值的详细解释: import okhttp3.Interceptor; import okhttp3.Request; import okhttp3.Response; import…...
Activiti(五)- 工作流引擎中流程定义删除机制
1、引言 Activiti作为一款轻量级、开源的工作流和业务流程管理(BPM)平台,在实际运维过程中,随着业务发展会产生大量需要清理的流程定义,不规范的删除操作可能导致数据不一致或系统异常等问题。本文将介绍Activiti中删除流程定义的相关方式及…...
C#里使用MaterialDesign来构造自定义窗口
本例子主要就是创建一个上面的界面, 它是一个采用MaterialDesign开源库来创建的WPF程序。 先要编辑App.xaml文件: <Application x:Class="MDIXWindow.App"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http:/…...
PyTorch 模型转换为 TensorRT 引擎的通用方法
PyTorch 模型转换为 TensorRT 引擎的通用方法 在深度学习模型的部署过程中,提升推理性能是一个重要的目标。将 PyTorch 模型(.pt 文件)转换为 TensorRT 引擎(.engine 文件)是一种常用的优化手段。本文将介绍几种通用的…...
利用Ruby的Typhoeus编写爬虫程序
Typhoeus是一个基于libcurl的HTTP客户端,支持并行请求,适合高效爬取数据。用户可能想要一个简单的例子,或者需要处理更复杂的情况,比如分页、并发请求或者数据解析。 首先,我应该检查用户是否已经安装了Typhoeus。通常…...
Fabric8 Kubernetes使用介绍
Fabric8 Kubernetes Client 是一个强大的 Java 客户端库,用于与 Kubernetes 集群交互。以下是快速上手指南: 1. 添加依赖 Maven 依赖: <dependency><groupId>io.fabric8</groupId><artifactId>kubernetes-client&…...
种田游戏的综合尝试
游戏角色 详细教程 一、准备工作 1、场景重命名为Farm 2、导入资源 (1) 新建文件夹。Import Asset (2) 导入:人物、走、跑、休息 3、设置摄像机 二、制作角色预制体 1、增加角色,命名为Player,设置材质、人类角色 2、设置角色的空闲…...
uniapp解决上架华为应用市场审核要求-监听权限的申请
支持android平台全局监听权限的申请。当申请权限时,会在页面顶部显示申请权限的目的。主要解决上架华为应用市场审核要求:APP在调用终端权限时,应同步告知用户申请该权限的目的。 因为如果不提示,你上架应用市场会被打打回来 Tip…...
【结肠息肉AI论文集】ASPS: Augmented Segment Anything Model for Polyp Segmentation
摘要 息肉分割在结直肠癌诊断中起着关键作用。最近,Segment Anything Model(SAM)的出现为息肉分割带来了前所未有的潜力,其在大规模数据集上的强大预训练能力使其备受关注。然而,由于自然图像和内窥镜图像之间存在领域…...
MHA详解
MHA(Master High Availability)是一个用于 MySQL 数据库的高可用性解决方案。它基于 MySQL 主从复制机制实现,通过自动化的方式进行故障检测和自动故障转移操作,确保在 MySQL 主节点(Master)发生故障时&…...
蓝桥杯单片机刷题——通过按键触发串口传输电压值
设计要求 通过内部ADC完成电位器RB2的输出电压检测,并显示在数码管上; 通过串口向PC端返回当前检测的电压值。 按键“S4”定义为发送按键,按下按键S4,串口向PC端发送当前检测的电压值。 串口发送格式: U:1.25V\r\…...
github fatal Authentication failed for解决
常用的生成令牌方式不多介绍,参考1 得到令牌后替换url 格式为 https://你的令牌github.com/<USERNAME>/<REPO>.git查看仓库url git remote -v假如为 https://github.com/jiang/megatron.git 令牌为CCCC 则将“令牌”插入github之前 使用 git remote …...
计算机网络基础知识
🧑 博主简介:CSDN博客专家,历代文学网(PC端可以访问:https://literature.sinhy.com/#/literature?__c1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,…...
Debian/Ubuntu Server高效禁用海外IP访问的一种方法
面对越来越多的海外IP扫描、攻击,让服务器损失大量的性能,影响服务器提供正常的对外服务,同时给自己的服务器处理风险之中,本文讲述了一种使用ufw防火墙的方式高效阻止海外IP的一种方法。 阻止IP访问的方法有很多种,这…...
2025年4月第2周 github的AI科技工具汇总
以下是2025年4月第2周GitHub上值得关注的AI科技工具与生态动态汇总,结合官方发布及开发者社区热点整理: AI编程工具升级 GitHub Copilot Agent Mode 全量发布 核心功能:在VS Code中启用后,可自主完成多文件代码重构、测试驱动开发…...
RIP V2路由协议配置实验CISCO
1.RIP V2简介: RIP V2(Routing Information Protocol Version 2)是 RIP 路由协议的第二版,属于距离矢量路由协议,主要用于中小型网络环境。相较于 RIP V1,RIP V2 在功能和性能上进行了多项改进,…...
Python-Django+vue二手电子设备交易平台功能说明
❥(^_-) 上千个精美定制模板,各类成品Java、Python、PHP、Android毕设项目,欢迎咨询。 ❥(^_-) 程序开发、技术解答、代码讲解、文档,💖文末获取源码+数据库+文档💖 💖软件下载 | 实战案例 💖文章底部二维码,可以联系获取软件下载链接,及项目演示视频。 本项目…...
DeepSeek 教我LLVM(2) : MCTargetDesc 核心模块有哪些?
在 LLVM 中,MCTargetDesc(Machine Code Target Description)是描述目标架构机器代码生成的核心模块,其核心组件可分为以下几类: 一、目标架构元数据 MCInstrInfo 功能:定义指令的元数据(操作数、…...
Linux:shell运行原理+权限
1.shell的运行原理 如果我们打开了命令终端或者是xshell进行远程登录服务器,就会看到命令行,如下图所示: 这个命令行本身也是系统中一个运行起来的程序,它用来接收用户的输入,帮用户来执行指令,将运行结果展…...
英伟达Llama-3.1-Nemotron-Ultra-253B-v1语言模型论文快读:FFN Fusion
FFN Fusion: Rethinking Sequential Computation in Large Language Models 代表模型:Llama-3.1-Nemotron-Ultra-253B-v1 1. 摘要 本文介绍了一种名为 FFN Fusion 的架构优化技术,旨在通过识别和利用自然并行化机会来减少大型语言模型(LLM…...
什么是A2A协议?什么是MCP协议?A2A和MCP协议哪个更好?
什么是A2A协议?什么是MCP协议?A2A和MCP协议哪个更好? 摘要 在人工智能领域,A2A协议(Agent-to-Agent协议)和MCP协议(Model Context Protocol)是两个重要的标准,它们分别…...
剑指Offer(数据结构与算法面试题精讲)C++版——day12
剑指Offer(数据结构与算法面试题精讲)C版——day12 题目一:小行星碰撞题目二:每日温度题目三:直方图最大矩形面积附录:源码gitee仓库 题目一:小行星碰撞 由题意可知,这里我们可使用栈…...
Leetcode131:分割回文串——回溯算法
Leetcode131:分割回文串——回溯算法 给一个字符串aab, 如何分割,使得子串 都是回文串,返回所有的分割方案。 答:1、aa,b。 2、a,a, b. 重点,想想树怎么画的。 发现:所有…...
Nginx常用工具
Nginx常用工具 Nginx常用工具vscode配置Nginx插件在线生成Nginx配置文件Nginx可视化配置工具 Nginx常用工具 编写Nginx配置时,使用VSCodeNginx插件,能实现自动补全格式化配置. vscode配置Nginx插件 Nginx代码高亮插件: nginx-formatter Nginx代码格式化插件&#…...
算法——回溯
学习目标: 掌握算法入门知识 学习内容: 回溯的定义例题详细步骤讲解(求子集、求全排列) 1. 回溯的定义 回溯法是一种通过 试探性搜索 来求解问题的算法思想。一个复杂问题的解决方案是由若干个小的决策步骤组成的决策序列&…...
【图片识别改名工具】如何识别图片中文字内容,并根据文字对图片批量重命名批量改名,基于WPF和腾讯OCR的完整实现
办公场景 批量处理图片文件:用户有一批图片文件,图片中包含文字信息(如编号、日期、名称等),需要根据图片中的文字内容对图片进行重命名。自动化办公:在办公场景中,用户需要将图片文件按内容分类或归档,手动重命名效率低下,自动化工具可以大幅…...
C语言超详细指针知识(一)
通过前面一段学习C语言的学习,我们了解了数组,函数,操作符等相关知识,今天我们将要进行指针学习,这是C语言中较难的一个部分,我将带你由浅入深慢慢学习。 1.内存与地址 在正式学习指针前,我们首…...
【实战手册】8000w数据迁移实践:MySQL到MongoDB的完整解决方案
🔥 本文将带你深入解析大规模数据迁移的实践方案,从架构设计到代码实现,手把手教你解决数据迁移过程中的各种挑战。 📚博主其他匠心之作,强推专栏: 小游戏开发【博主强推 匠心之作 拿来即用无门槛】文章目录 一、场景引入1. 问题背景2. 场景分析为什么需要消息队列?为…...
本地部署DeepSeek-R1,搭建本地知识库
本地部署DeepSeek-R1,搭建本地知识库 本章节内容是参照哔哩哔哩上的【一拳S7】作者分享的视频教程(【喂饭教程】20分钟教会你本地部署DeepSeek-R1,并搭建自己的知识库!小白也能轻松上手!!)搭建的。 概述:本…...
多链协议和跨链桥
文章目录 多链协议和跨链桥一、核心功能与分类二、主流协议技术对比三、关键技术机制四、典型应用场景五、核心风险与挑战 多链协议和跨链桥 一、核心功能与分类 多链协议与跨链桥旨在解决区块链生态的孤岛效应,实现资产与数据的跨链互通。 主要分为两类…...
2025.4.9 华为机考 第1题-补丁版本升级
目录 题目内容样例1样例2思考思路算法思路实现代码(封装Class中) 题目内容 样例1 样例2 思考思路 题目让我们找出所有迭代次数最多的补丁版本,并且按字典序排列输出。迭代次数指的是从该版本到根节点的路径长度,而根节点的迭代次…...
Qt平台+三维建模+动画演示+工业风展示
三维建模不止于形,界面定制不止于皮,实时交互不止于快。 我们专注程序界面定制,尤其擅长: 🔧 Qt平台 精准开发 🧊 三维建模 动画演示 工业风展示 ⚡ 实时交互系统 支持多线程响应、动态数据绑定 …...
通付盾风控智能体(RiskAgent): 神烦狗(DOGE)
在数字化业务高速发展的今天,风控系统已成为企业抵御黑产、欺诈、保障交易安全的核心防线。然而传统风控面临人力依赖高与策略滞后性等挑战,数据分析师需每日从海量数据中手动提炼风险特征、设计防护规则,耗时费力;新策略从发现到…...
Anaconda环境管理及 pycharm、jupyter notebook 的配置
为什么要Anaconda环境管理,因为不同的项目可能使用的环境是不一样的,比如说有一个项目要用到pytorch0.4,另一个要用到pytorch1.0,我们不可能在一个环境中既装pytorch0.4,又装pytorch1.0,因此我们在用pytorc…...
DevOps与功能安全:Perforce ALM通过ISO 26262合规认证,简化安全关键系统开发流程
本文来源perforce.com,由Perforce中国授权合作伙伴、DevSecOps解决方案提供商-龙智翻译整理。 近日,Perforce ALM(原Helix ALM)通过了国际权威认证机构 TV SD的ISO 26262功能安全流程认证!该认证涵盖Perforce ALM解决方…...
OpenCV 伪彩色处理
伪彩色处理是将灰度图像通过颜色映射转换为彩色图像的技术,常用于增强可视化效果或突出显示特定灰度范围。OpenCV 提供了多种方法实现伪彩色处理。 1. 使用 applyColorMap 函数 OpenCV 提供了 cv::applyColorMap() 函数,支持多种预定义的色彩映射&…...
【物联网】PWM控制蜂鸣器
文章目录 一、PWM介绍1.PWM的频率2.PWM的周期 二、PWM工作原理分析三、I.MX6ull PWM介绍1.时钟信号2.工作原理3.FIFO 四、PWM重点寄存器介绍1.PWM Control Register (PWMx_PWMCR)2.PWM Counter Register (PWMx_PWMCNR)3.PWM Period Register (PWMx_PWMPR)4.PWM Sample Register…...
centos-stream-9上安装nvidia驱动和cuda-toolkit
这里写目录标题 驱动安装1. 更新系统2. NVIDIA GPU安装检查系统是否安装了 NVIDIA GPU2.1 首先,使用以下命令更新 DNF 软件包存储库缓存:2.2 安装编译 NVIDIA 内核模块所需的依赖项和构建工具2.3 在 CentOS Stream 9 上添加官方 NVIDIA CUDA 软件包存储库…...
嵌入式---烧录器
一、核心定义与本质功能 烧录器(Programmer)是一种将用户编写的程序代码(如.hex/.bin文件)写入单片机内部存储器(Flash/EEPROM/ROM)的专用工具,核心功能包括: 程序烧写:…...
centos-LLM-生物信息-BioGPT-使用1
参考: GitHub - microsoft/BioGPT https://github.com/microsoft/BioGPT BioGPT:用于生物医学文本生成和挖掘的生成式预训练转换器 |生物信息学简报 |牛津学术 — BioGPT: generative pre-trained transformer for biomedical text generation and mini…...
Golang|协程
文章目录 go func 和 WaitGroup协程 v.s. 线程GMP 并发模型 go func 和 WaitGroup 在 Go 语言中,协程(goroutine)之间并没有严格的父子关系。一个协程中可以启动其他协程,即使启动它的协程已经结束,所启动的新协程仍会…...
免费下载地图切片数据以及通过CesiumEarth在Windows和安卓本地浏览
免费下载地图切片数据 本文概述 互联网存在许多免费的地图资源,但是这些资源都需要在互联网环境下使用,如果使用场景是在离线或本地设备上,没有在线调用地图的途径,这时如果没有自己制作地图的能力,只能提前下载地…...
Enovia许可使用限制
在数字化时代,企业的研发与创新活动越来越依赖于先进的软件工具。Enovia,作为一款功能强大的产品生命周期管理(PLM)解决方案,为众多行业的企业提供了从概念到生产的全方位支持。然而,为了确保软件的合规性与…...
“米其林.网址”中文域名仲裁案:知名企业如何保护网络知产权益?
中文域名与商标协同保护经典案例解析 近期,国际知名的轮胎制造商米其林公司(Compagnie Gnrale des tablissements Michelin)通过仲裁程序成功夺回了被抢注的中文域名<米其林.网址>。这一案件再次引发了公众对企业网络知识产权保护和域名…...
TCP专题
一、TCP连接建立 1、TCP的四元组 TCP属于面向连接——在发送数据之前,需要先建立一条点到点的连接。 TCP的四元组:源IP地址、目标IP地址、源端口、目标端口。可以唯一的区分和标识一条TCP的连接。 2、TCP报文结构 序列号:TCP是基于”字节…...
力扣热题100刷题day63|49.字母异位词分组
一、哈希表相关理论 代码随想录刷题day15|(哈希表篇)242.有效的字母异位词、383.赎金信-CSDN博客 二、思路 首先,创建一个map集合,遍历字符串数组,对数组中每一个字符串(单词)比如"abc&…...
BERT - 今日头条新闻分类任务实战
1. 自定义模型组件 MultiHeadAttention 类 实现了多头自注意力机制。 通过将输入分割成多个“头”,从不同角度学习输入数据的特征。 注意力分数计算后应用了缩放点积注意力,并支持掩码操作。 class MultiHeadAttention(nn.Module):def __init__(se…...
leetcode-419.棋盘上的战舰
leetcode-419.棋盘上的战舰 文章目录 leetcode-419.棋盘上的战舰一.题目描述二.第一次代码提交三.第二次代码提交 一.题目描述 二.第一次代码提交 class Solution { public:int countBattleships(vector<vector<char>>& board) {int m board.size(); //列数i…...
Python爬虫第11节-解析库Beautiful Soup的使用上篇
目录 前言 一、Beautiful Soup 简介 1.1 Beautiful Soup概述 1.2 准备工作 1.3 解析器 二、基本使用 三、节点选择器的使用 3.1 选择元素 3.2 提取信息 3.2.1 获取名称 3.2.2 获取属性 3.2.3 获取内容 3.3 嵌套选择 3.4 关联选择 3.4.1 子节点和子孙节点 3.4.2…...
host模式容器compose建立记录
需要一个host模式的容器,用来跑gpu程序。同时最好使用ssh来直接远程连接。 以前是直接建立容器然后手动安装ssh的。但是最近突然又不行了,不知道为什么。我知道原因是容器的systmctl不能使用。但是为什么以前能用呢? 一、安装结构 总之换成…...