文章基于 OkHttp3.14.3 版本
OkHttp 作为当下Java系编程的网络请求库,其热门程度自不必说了。网上有关OkHttp的使用、封装和源码分析的文章和代码也早已是百家齐放、甚至可以说是烂大街了。然而尽管如此,笔者还是希望能够将对OkHttp的学习和研究记录下来形成自己的内容,方便以后查看,于是开始写吧,好记性不如烂笔头。
那就从最简单的开始。这篇文章打算简要描述一下OkHttp中大致的请求响应过程。
首先看看简单的同步GET请求和异步GET请求:
public static void getSync() {
// Step 1. 创建一个HttpClient实例用于创建请求任务
OkHttpClient httpClient = new OkHttpClient();
// Step 2. 构建一个Request用于封装请求地址、请求类型、参数等信息
Request request = new Request.Builder().get()
.url("https://www.baidu.com")
.build();
// Step 3. 创建一个新的请求任务Call
Call call = httpClient.newCall(request);
try {
// Step 4. 发起请求
Response response = call.execute();
// Step 5. 读取、处理请求结果
ResponseBody responseBody = response.body();
if (responseBody != null) {
System.out.println(responseBody.string());
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void getAsync() {
// Step 1. 创建一个HttpClient实例用于创建请求任务
OkHttpClient httpClient = new OkHttpClient();
// Step 2. 构建一个Request用于封装请求地址、请求类型、参数等信息
Request request = new Request.Builder().get()
.url("https://www.baidu.com")
.build();
// Step 3. 创建一个新的请求任务Call
Call call = httpClient.newCall(request);
// Step 4. 发起请求
call.enqueue(new Callback() {
@Override
public void onFailure(final Call call, final IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(final Call call, final Response response) throws IOException {
// Step 5. 读取、处理请求结果
ResponseBody responseBody = response.body();
if (responseBody != null) {
System.out.println(responseBody.string());
}
}
});
}
可以看到,不管是同步还是异步请求,都需要经过Step1~Step3三个步骤构建一个请求任务,并通过调用call.execute()/call.enqueue(callback)来执行同步/异步请求。那接下来就看看这两个方法的执行过程吧。
点击查看Call类,发现Call是一个接口,尝试跳转到call.execute()方法的具体实现( ps: AS快捷键Ctrl+Alt+B实现快速跳转到方法的具体实现 ),来到Call接口的唯一实现类 RealCall 类,RealCall.execute()具体实现如下:
@Override public Response execute() throws IOException {
// Step 1.
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
// Step 2.
transmitter.timeoutEnter();
transmitter.callStart();
try {
// Step 3.
client.dispatcher().executed(this);
// Step 4.
return getResponseWithInterceptorChain();
} finally {
// Step 5.
client.dispatcher().finished(this);
}
}
/** 正在运行的同步请求任务,包括尚未结束就已经取消同步请求. */
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
经过以上这几个步骤,一次同步GET请求就算是结束了。可以发现,RealCall.getResponseWithInterceptorChain()方法负责进行具体的HTTP请求,这里暂时不跟进去,先来 看看异步的GET请求 :
Ctrl+Alt+B 快速跳转到 call.enqueue(callback) 的具体实现RealCall.enqueue(callback):
@Override public void enqueue(Callback responseCallback) {
// Step 1.
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
// Step 2.
transmitter.callStart();
// Step 3.
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
@Override protected void execute() {
boolean signalledCallback = false;
// Step 3.1 开始计算请求超时时间
transmitter.timeoutEnter();
try {
// Step 3.2 发起请求并获取请求结果
Response response = getResponseWithInterceptorChain();
signalledCallback = true;
// Step 3.3 请求成功,将结果通过回调接口返回给上层
responseCallback.onResponse(RealCall.this, response);
} catch (IOException e) {
if (signalledCallback) {
// Do not signal the callback twice!
Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
} else {
// 请求失败
responseCallback.onFailure(RealCall.this, e);
}
} finally {
// Step 4.结束本次请求任务,并从队列中移除
client.dispatcher().finished(this);
}
}
}
而查阅execute的实现也验证了这个判定是正确的,至于何时,从哪儿,会执行到这个execute方法,先留个小坑吧后续再填~。同时,对比RealCall.execute()方法(同步请求)和AsyncCall.execute()方法(异步请求)发现,不管是同步还是异步请求,最后都会通过调用getResponseWithInterceptorChain()方法来实现网络请求求并获取返回结果,看看这个方法的实现:
Response getResponseWithInterceptorChain() throws IOException {
// 构建一个完整的拦截器列表
List<Interceptor> interceptors = new ArrayList<>();
// 添加用户自定义的拦截器
interceptors.addAll(client.interceptors());
// 添加用于失败重试和重定向的拦截器
interceptors.add(new RetryAndFollowUpInterceptor(client));
// 添加用于连接应用层和网络层的拦截器,该拦截器会将一个用户请求转换为网络请求,并将网络请求结果以用户友好的方式返回
interceptors.add(new BridgeInterceptor(client.cookieJar()));
// 添加用于缓存的拦截器
interceptors.add(new CacheInterceptor(client.internalCache()));
// 添加用于打开与服务器间网络连接的拦截器
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
// 如果本次请求任务是websocket请求,则添加用于处理websocket请求的拦截器,该拦截器也需要用户自定义
interceptors.addAll(client.networkInterceptors());
}
// 添加最后一个拦截器,该拦截器负责执行最终的网络请求并返回结果
interceptors.add(new CallServerInterceptor(forWebSocket));
// 构建一个拦截器链chain对象
Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0,
originalRequest, this, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
boolean calledNoMoreExchanges = false;
try {
// 执行请求并获取返回结果
Response response = chain.proceed(originalRequest);
if (transmitter.isCanceled()) {
closeQuietly(response);
throw new IOException("Canceled");
}
return response;
} catch (IOException e) {
calledNoMoreExchanges = true;
throw transmitter.noMoreExchanges(e);
} finally {
if (!calledNoMoreExchanges) {
transmitter.noMoreExchanges(null);
}
}
}
可以看到,这个方法最终是通过chain.proceed(originalRequest);实现请求和返回结果,到这里,OkHttp的请求过程就结束了。因此,一个OkHttp的请求过程大致如下:
先到这里吧。本来想着一篇写完的但太长了似乎自己都不想看,还是一步步来吧。下一篇开始着重分析以上OkHttp请求过程中接触到的各个关键的类。
欢迎关注公众号:
文章首发在个人博客 https://www.nullobject.cn ,公众号 NullObject 同步更新。