1.1 OkHttp整体架构设计
1.1.1 核心设计理念
OkHttp的设计遵循了以下几个核心原则:
- 分层架构:将网络请求分解为多个独立组件
- 责任链模式:通过拦截器链实现请求处理的模块化
- 连接复用:通过连接池减少TCP握手开销
- 异步优先:默认支持高效的非阻塞I/O操作
1.1.2 核心组件关系
1.2 OkHttpClient初始化过程
1.2.1 构建者模式源码解析
// 源码路径: okhttp3/OkHttpClient.java
public class OkHttpClient implements Cloneable, Call.Factory {
final Dispatcher dispatcher;
final ConnectionPool connectionPool;
final List<Interceptor> interceptors;
public OkHttpClient() {
this(new Builder());
}
OkHttpClient(Builder builder) {
// 1. 调度器初始化
this.dispatcher = builder.dispatcher != null ? builder.dispatcher : new Dispatcher();
// 2. 连接池初始化
this.connectionPool = builder.connectionPool != null ?
builder.connectionPool : new ConnectionPool();
// 3. 拦截器列表初始化
this.interceptors = Util.immutableList(builder.interceptors);
// 4. 超时设置
this.connectTimeout = builder.connectTimeout;
this.readTimeout = builder.readTimeout;
this.writeTimeout = builder.writeTimeout;
// ...其他参数初始化
}
// 构建者类
public static final class Builder {
Dispatcher dispatcher;
ConnectionPool connectionPool;
final List<Interceptor> interceptors = new ArrayList<>();
int connectTimeout;
int readTimeout;
int writeTimeout;
public Builder() {
// 默认值设置
this.dispatcher = new Dispatcher();
this.connectionPool = new ConnectionPool();
this.connectTimeout = 10_000; // 10秒
this.readTimeout = 10_000; // 10秒
this.writeTimeout = 10_000; // 10秒
}
public Builder connectTimeout(long timeout, TimeUnit unit) {
// 参数验证
if (timeout < 0) throw new IllegalArgumentException("timeout < 0");
if (unit == null) throw new NullPointerException("unit == null");
// 转换为毫秒
long millis = unit.toMillis(timeout);
if (millis > Integer.MAX_VALUE) {
throw new IllegalArgumentException("Timeout too large.");
}
this.connectTimeout = (int) millis;
return this;
}
// 其他设置方法类似...
public OkHttpClient build() {
return new OkHttpClient(this);
}
}
}
1.2.2 关键配置项说明
| 配置项 | 默认值 | 说明 |
|---|---|---|
dispatcher | new Dispatcher() | 请求调度器,管理异步请求队列 |
connectionPool | new ConnectionPool() | 连接池,默认最大空闲连接数5,存活时间5分钟 |
connectTimeout | 10秒 | TCP连接建立超时时间 |
readTimeout | 10秒 | 读取响应超时时间 |
writeTimeout | 10秒 | 发送请求超时时间 |
interceptors | 空列表 | 全局拦截器,在所有拦截器之前执行 |
1.3 Request请求构建过程
1.3.1 Request类结构解析
// 源码路径: okhttp3/Request.java
public class Request {
final HttpUrl url;
final String method;
final Headers headers;
final RequestBody body;
final Object tag;
Request(Builder builder) {
this.url = builder.url;
this.method = builder.method;
this.headers = builder.headers.build();
this.body = builder.body;
this.tag = builder.tag != null ? builder.tag : this;
}
public static class Builder {
HttpUrl url;
String method;
Headers.Builder headers;
RequestBody body;
Object tag;
public Builder() {
this.method = "GET";
this.headers = new Headers.Builder();
}
public Builder url(HttpUrl url) {
if (url == null) throw new NullPointerException("url == null");
this.url = url;
return this;
}
public Builder url(String url) {
if (url == null) throw new NullPointerException("url == null");
// 解析URL并验证
if (url.regionMatches(true, 0, "ws:", 0, 3)) {
url = "http:" + url.substring(3);
} else if (url.regionMatches(true, 0, "wss:", 0, 4)) {
url = "https:" + url.substring(4);
}
return url(HttpUrl.get(url));
}
public Builder header(String name, String value) {
headers.set(name, value);
return this;
}
public Builder addHeader(String name, String value) {
headers.add(name, value);
return this;
}
public Builder post(RequestBody body) {
return method("POST", body);
}
public Builder method(String method, RequestBody body) {
// 方法验证
if (method == null) throw new NullPointerException("method == null");
if (method.length() == 0) throw new IllegalArgumentException("method.length() == 0");
// 对于需要请求体的方法验证
if (body != null) {
if (!HttpMethod.permitsRequestBody(method)) {
throw new IllegalArgumentException("method " + method + " must not have a request body.");
}
} else if (HttpMethod.requiresRequestBody(method)) {
throw new IllegalArgumentException("method " + method + " must have a request body.");
}
this.method = method;
this.body = body;
return this;
}
public Request build() {
if (url == null) throw new IllegalStateException("url == null");
return new Request(this);
}
}
}
1.3.2 RequestBody类型体系
1.3.3 请求构建实践
// 1. GET请求示例
Request getRequest = new Request.Builder()
.url("https://api.example.com/users")
.header("Authorization", "Bearer token123")
.build();
// 2. POST表单请求
RequestBody formBody = new FormBody.Builder()
.add("username", "john_doe")
.add("password", "secret")
.build();
Request postRequest = new Request.Builder()
.url("https://api.example.com/login")
.post(formBody)
.build();
// 3. POST JSON请求
MediaType JSON = MediaType.get("application/json; charset=utf-8");
String json = "{"name":"John", "age":30}";
RequestBody jsonBody = RequestBody.create(json, JSON);
Request jsonRequest = new Request.Builder()
.url("https://api.example.com/users")
.post(jsonBody)
.build();
1.4 核心类职责详解
1.4.1 OkHttpClient核心职责
- 请求调度:通过Dispatcher管理请求队列
- 连接管理:通过ConnectionPool复用HTTP连接
- 协议支持:处理HTTP/1.1、HTTP/2和WebSocket
- 超时控制:管理连接、读写超时
- 拦截器链:组织请求处理流程
1.4.2 Dispatcher工作原理
Dispatcher管理三个队列:
- runningAsyncCalls:正在执行的异步请求
- readyAsyncCalls:等待执行的异步请求
- runningSyncCalls:正在执行的同步请求
默认并发策略:
- 最大并发请求数:64
- 每个主机最大并发数:5
1.4.3 ConnectionPool连接复用机制
连接池维护TCP连接,减少重复握手开销:
- 最大空闲连接数:5
- 空闲连接存活时间:5分钟
- 自动清理过期连接
1.5 最佳实践:OkHttpClient单例模式
1.5.1 为什么要使用单例?
// 错误做法:每次请求创建新客户端
public void fetchData(String url) {
OkHttpClient client = new OkHttpClient(); // 每次创建新实例
Request request = new Request.Builder().url(url).build();
try (Response response = client.newCall(request).execute()) {
// 处理响应
}
}
问题分析:
- 每次请求都创建新线程池,增加系统开销
- 无法复用TCP连接,失去连接池优势
- 可能导致文件描述符耗尽
1.5.2 正确实现方式
// 单例实现
public class HttpClient {
private static final OkHttpClient INSTANCE = new OkHttpClient.Builder()
.connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(15, TimeUnit.SECONDS)
.connectionPool(new ConnectionPool(10, 5, TimeUnit.MINUTES))
.build();
public static OkHttpClient getInstance() {
return INSTANCE;
}
}
// 使用示例
public void fetchUserData() {
OkHttpClient client = HttpClient.getInstance();
Request request = new Request.Builder()
.url("https://api.example.com/users")
.build();
try (Response response = client.newCall(request).execute()) {
// 处理响应
}
}
1.5.3 自定义配置单例
public class CustomHttpClient {
private static volatile OkHttpClient instance;
public static OkHttpClient getInstance() {
if (instance == null) {
synchronized (CustomHttpClient.class) {
if (instance == null) {
instance = createClient();
}
}
}
return instance;
}
private static OkHttpClient createClient() {
// 创建自定义DNS解析器
Dns customDns = hostname -> {
// 自定义DNS解析逻辑
return InetAddress.getAllByName(hostname);
};
// 创建线程安全的CookieJar
CookieJar cookieJar = new CookieJar() {
private final Map<HttpUrl, List<Cookie>> cookieStore = new ConcurrentHashMap<>();
@Override
public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
cookieStore.put(url, cookies);
}
@Override
public List<Cookie> loadForRequest(HttpUrl url) {
return cookieStore.getOrDefault(url, Collections.emptyList());
}
};
// 构建客户端
return new OkHttpClient.Builder()
.connectTimeout(20, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.dns(customDns)
.cookieJar(cookieJar)
.addInterceptor(new LoggingInterceptor())
.build();
}
}
1.6 初始化过程性能优化
1.6.1 关键配置优化项
OkHttpClient client = new OkHttpClient.Builder()
// 1. 连接池优化
.connectionPool(new ConnectionPool(
10, // 最大空闲连接数
5, // 保持存活时间(分钟)
TimeUnit.MINUTES))
// 2. 超时设置优化
.connectTimeout(15, TimeUnit.SECONDS) // 连接超时
.readTimeout(30, TimeUnit.SECONDS) // 读取超时
.writeTimeout(30, TimeUnit.SECONDS) // 写入超时
// 3. 协议配置优化
.protocols(Arrays.asList(Protocol.HTTP_2, Protocol.HTTP_1_1))
// 4. 连接规范优化
.connectionSpecs(Arrays.asList(
ConnectionSpec.MODERN_TLS,
ConnectionSpec.CLEARTEXT))
// 5. 自定义DNS解析
.dns(new CustomDnsResolver())
// 6. 事件监听器(用于监控)
.eventListener(new PerformanceEventListener())
.build();
1.6.2 DNS解析优化
public class CustomDnsResolver implements Dns {
private static final long CACHE_DURATION = 5 * 60 * 1000; // 5分钟缓存
private final Map<String, DnsCacheEntry> cache = new ConcurrentHashMap<>();
@Override
public List<InetAddress> lookup(String hostname) throws UnknownHostException {
// 1. 检查缓存
DnsCacheEntry cached = cache.get(hostname);
if (cached != null && !cached.isExpired()) {
return cached.addresses;
}
// 2. 系统DNS解析
List<InetAddress> addresses = Dns.SYSTEM.lookup(hostname);
// 3. 缓存结果
cache.put(hostname, new DnsCacheEntry(addresses));
return addresses;
}
private static class DnsCacheEntry {
final List<InetAddress> addresses;
final long expireTime;
DnsCacheEntry(List<InetAddress> addresses) {
this.addresses = addresses;
this.expireTime = System.currentTimeMillis() + CACHE_DURATION;
}
boolean isExpired() {
return System.currentTimeMillis() > expireTime;
}
}
}
1.7 核心设计模式应用
1.7.1 构建者模式(Builder Pattern)
-
应用场景:创建复杂对象(OkHttpClient, Request)
-
优势:
- 分离对象的构建与表示
- 支持链式调用,提高可读性
- 允许创建不可变对象
1.7.2 工厂模式(Factory Pattern)
- 应用场景:Call对象创建
// OkHttpClient实现了Call.Factory接口
public interface Call.Factory {
Call newCall(Request request);
}
// 实现
public class OkHttpClient implements Call.Factory {
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false);
}
}
1.7.3 责任链模式(Chain of Responsibility)
-
应用场景:拦截器链处理请求
-
优势:
- 解耦请求发送者和接收者
- 动态添加处理逻辑
- 支持请求/响应的预处理和后处理
1.8 常见问题与解决方案
问题1:如何添加全局请求头?
解决方案:使用应用拦截器
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(chain -> {
Request original = chain.request();
Request modified = original.newBuilder()
.header("User-Agent", "MyApp/1.0")
.header("Authorization", "Bearer token")
.build();
return chain.proceed(modified);
})
.build();
问题2:如何实现统一错误处理?
解决方案:使用网络拦截器
OkHttpClient client = new OkHttpClient.Builder()
.addNetworkInterceptor(chain -> {
Response response = chain.proceed(chain.request());
if (!response.isSuccessful()) {
throw new HttpException(response.code(), response.message());
}
return response;
})
.build();
问题3:如何启用响应缓存?
解决方案:配置缓存目录
// 创建10MB缓存
int cacheSize = 10 * 1024 * 1024;
File cacheDirectory = new File(context.getCacheDir(), "http-cache");
Cache cache = new Cache(cacheDirectory, cacheSize);
OkHttpClient client = new OkHttpClient.Builder()
.cache(cache)
.build();
本章小结
第一章深入分析了OkHttp的核心架构与初始化过程:
- OkHttpClient是核心入口,采用构建者模式配置
- Request封装HTTP请求,支持多种请求类型
- Dispatcher管理请求队列和线程池
- ConnectionPool实现TCP连接复用
- 单例模式是使用OkHttp的最佳实践
- 合理配置超时、连接池和DNS可提升性能
在下一章中,我们将深入探讨请求调度与执行机制,分析同步和异步请求的内部实现原理。