24、网络请求之OkHttp:OkHttp基本使用、拦截器、缓存、连接池配置
OkHttp,说实话,是Android开发中绕不开的一个网络库。我最早接触它的时候,还在用HttpURLConnection手写请求,那叫一个痛苦。后来项目里引入了OkHttp,我才发现原来网络请求可以这么优雅。今天我们就来聊聊它的核心玩法。
一、OkHttp的基本使用
先看一个最简单的GET请求怎么写。我个人习惯把OkHttpClient做成单例,避免反复创建带来的资源浪费。
// 创建OkHttpClient实例
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.build();
// 构建请求
Request request = new Request.Builder()
.url("https://api.example.com/users")
.get()
.build();
// 同步请求(注意:不能在主线程调用)
try {
Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
String body = response.body().string();
// 处理数据
}
} catch (IOException e) {
e.printStackTrace();
}
// 异步请求(推荐)
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
// 网络错误处理
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
String body = response.body().string();
// 注意:这里是在子线程,更新UI需要切回主线程
}
}
});
嗯,这里要注意一点:response.body().string()只能调用一次。为什么?因为OkHttp的响应体本质是一个流,读完就没了。我在项目中遇到过有人重复调用导致返回空字符串的坑,排查了半天。
二、拦截器:请求的“中间人”
拦截器是OkHttp最强大的特性之一。说白了,它就像一道流水线,每个请求和响应都会经过你定义的拦截器。你可以在这里做日志打印、添加公共Header、重试逻辑等等。
我通常把拦截器分为两类:
- 应用拦截器(addInterceptor):在请求发出前、响应返回后执行,能看到完整的请求和响应。
- 网络拦截器(addNetworkInterceptor):在真正发起网络请求时执行,能看到重定向和重试的细节。
// 日志拦截器
class LoggingInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
long startTime = System.nanoTime();
// 打印请求信息
Log.d("OkHttp", String.format("发送请求 %s %s",
request.method(), request.url()));
Response response = chain.proceed(request);
long endTime = System.nanoTime();
// 打印响应信息
Log.d("OkHttp", String.format("收到响应 %s 耗时 %.1fms",
response.code(), (endTime - startTime) / 1e6));
return response;
}
}
// 添加公共Header的拦截器
class AuthInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request original = chain.request();
Request newRequest = original.newBuilder()
.header("Authorization", "Bearer " + getToken())
.header("Accept-Language", "zh-CN")
.build();
return chain.proceed(newRequest);
}
}
// 使用
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new AuthInterceptor()) // 应用拦截器
.addNetworkInterceptor(new LoggingInterceptor()) // 网络拦截器
.build();
三、缓存策略:让数据“快”起来
缓存是提升用户体验的关键。你想想看,用户每次打开App都要重新请求数据,那体验得多差。OkHttp的缓存机制非常灵活,它遵循HTTP的缓存规范。
配置缓存其实就两步:
- 创建Cache目录和大小
- 在OkHttpClient中配置
// 创建缓存目录
File cacheDir = new File(context.getCacheDir(), "okhttp_cache");
int cacheSize = 10 * 1024 * 1024; // 10MB
Cache cache = new Cache(cacheDir, cacheSize);
// 配置OkHttpClient
OkHttpClient client = new OkHttpClient.Builder()
.cache(cache)
.build();
// 服务端控制缓存(通过响应头)
// 例如服务端返回:Cache-Control: max-age=3600
// 表示该资源在1小时内可以直接使用缓存
// 客户端强制使用缓存(离线模式)
Request request = new Request.Builder()
.url("https://api.example.com/data")
.header("Cache-Control", "only-if-cached")
.build();
四、连接池配置:复用连接,减少开销
连接池是OkHttp性能优化的关键。说白了,就是复用TCP连接,避免每次请求都重新建立连接。三次握手的开销可不小。
OkHttp默认的连接池配置是:最大空闲连接数5个,每个连接最多保持5分钟。对于大多数应用来说,这个配置已经够用了。但如果你有特殊需求,可以调整:
OkHttpClient client = new OkHttpClient.Builder()
.connectionPool(new ConnectionPool(
10, // 最大空闲连接数
10, // 保持存活时间
TimeUnit.MINUTES
))
.build();
为什么需要调整?我举个例子。如果你做的是一个直播类App,频繁的短连接请求,默认的5个空闲连接可能不够用。这时候适当增大连接池,能明显减少连接建立的时间。
核心要点:
- 连接池只复用空闲连接,正在使用的连接不会被回收
- 保持存活时间不宜过长,否则会占用系统资源
- 如果App主要做长连接(如WebSocket),连接池的作用不大
五、知识体系总览
下面这张图是我自己整理的OkHttp核心知识结构,你可以对照着回顾一下:
六、避坑指南与最佳实践
最后,分享几个我实际项目中的经验:
- 不要在主线程执行同步请求——这会导致ANR,我见过太多新手犯这个错了。
- 合理设置超时时间——连接超时建议5-10秒,读取超时建议10-30秒。太短容易误判,太长浪费用户时间。
- 缓存目录要放在私有目录——不要放在SD卡根目录,否则容易被用户清理掉。
- 连接池大小不是越大越好——每个空闲连接都会占用内存和文件描述符,默认5个已经够用。
OkHttp虽然功能强大,但核心就这些。你只要掌握了基本使用、拦截器、缓存和连接池,就能应对绝大多数场景了。剩下的,就是在实战中慢慢积累经验了。
公众号:蓝海资料掘金营,微信deep3321