OkHttp 开源库使用与源码解析
关于 OkHttp
OkHttp 是一个适用于 Android 和 Java 应用程序的 HTTP+HTTP/2客户端。
如何使用
Android 开发只需添加依赖,如下:
implementation("com.squareup.okhttp3:okhttp:3.13.1")
官方示例1:获取一个 url 上的内容并输出
//Http 客户端
OkHttpClient client = new OkHttpClient();
String run(String url) throws IOException {
//构造请求
Request request = new Request.Builder()
.url(url)
.build();
//执行请求,获取数据
try (Response response = client.newCall(request).execute()) {
return response.body().string();
}
}
官方示例2:给服务器post
数据
public static final MediaType JSON
= MediaType.get("application/json; charset=utf-8");
OkHttpClient client = new OkHttpClient();
String post(String url, String json) throws IOException {
RequestBody body = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
try (Response response = client.newCall(request).execute()) {
return response.body().string();
}
}
简单分析一下这段代码:这个post
请求操作看起来很简单。但是我们需要学习其中几个很重要的接口:
OKHttpClient:它代表着 http 客户端 Request:它封装了请求对象,可以构造一个 http 请求对象 Response:封装了响应结果 Call:client.newCall()调用后生成一个请求执行对象Call,它封装了请求执行过程。
下面我们结合这个例子来分析源码:
newCall().execute()
跟踪源码后发现这个方法是在 Call 中的接口,代码如下:
/**
* A call is a request that has been prepared for execution. A call can be canceled. As this object
* represents a single request/response pair (stream), it cannot be executed twice.
*/
public interface Call extends Cloneable {
//省略部分代码
//同步执行请求
Response execute() throws IOException;
//请求加入队列
void enqueue(Callback responseCallback);
//省略部分代码
}
Call 的实现类是 RealCall,继续追踪源码的 RealCall.java 文件,可以看到 execute 方法:
@Override public Response execute() throws IOException {
//同步锁检查该请求是否已经执行,如果没有则标记executed = ture,否则抛出异常
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
timeout.enter();
//调用了回调方法 callStart
eventListener.callStart(this);
try {
//okhttp 客户端调用 dispatcher 将执行请求对象
client.dispatcher().executed(this);
//调用了 getResponseWithInterceptorChain 方法获取到响应数据 Response,后期还会继续分析
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
e = timeoutExit(e);
//请求失败的回调 callFailed
eventListener.callFailed(this, e);
throw e;
} finally {
client.dispatcher().finished(this);
//使用 dispather 对象调用 finished 方法,完成请求
}
}
接下来我们详细分析一下 dispatcher.execute 和 getResponseWithInterceptorChain 这两个方法:
跟踪源码 Dispatcher
public final class Dispatcher {
//省略部分代码
/** Executes calls. Created lazily. */
private @Nullable ExecutorService executorService;
/** Ready async calls in the order they'll be run. */
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
public Dispatcher(ExecutorService executorService) {
this.executorService = executorService;
}
void enqueue(AsyncCall call) {
synchronized (this) {
readyAsyncCalls.add(call);
// Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to
// the same host.
if (!call.get().forWebSocket) {
AsyncCall existingCall = findExistingCallWithHost(call.host());
if (existingCall != null) call.reuseCallsPerHostFrom(existingCall);
}
}
promoteAndExecute();
}
private <T> void finished(Deque<T> calls, T call) {
Runnable idleCallback;
synchronized (this) {
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
idleCallback = this.idleCallback;
}
boolean isRunning = promoteAndExecute();
if (!isRunning && idleCallback != null) {
idleCallback.run();
}
}
}
发现 Dispatcher 是一个调度器,它的作用是对请求进行分发。它的内部有三个队列,分别是同步请求进行队列、异步请求等待队列、异步请求执行队列。
核心方法:getResponseWithInterceptorChain
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());//添加自定义拦截器
interceptors.add(retryAndFollowUpInterceptor);//重试重定向拦截器
interceptors.add(new BridgeInterceptor(client.cookieJar()));
interceptors.add(new CacheInterceptor(client.internalCache()));
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));//调用服务器拦截
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
//开始责任链模式
return chain.proceed(originalRequest);
}
此方法构建数个拦截器之后,又构造了一个拦截器责任链。跟踪源码 RealInterceptorChain:
RealInterceptorChain
该类主要负责将所有的拦截器串连起来,使所有的拦截器以递归的方式进行实现,从而确保只有所有的拦截器都执行完之后才会返回 Response。以下对该类中的主要方法 proceed 进行讲解:
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
if (index >= interceptors.size()) throw new AssertionError();
//用于计算拦截器调用该方法的次数
calls++;
// If we already have a stream, confirm that the incoming request will use it.
if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must retain the same host and port");
}
// If we already have a stream, confirm that this is the only call to chain.proceed().
if (this.httpCodec != null && calls > 1) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must call proceed() exactly once");
}
//生成下一个拦截器调动该方法的 RealInterceptorChain 对象,其中 index+1 用于获取下一个拦截器
RealInterceptorChain next = new RealInterceptorChain(
interceptors, streamAllocation, httpCodec, connection, index + 1, request);
//获取当前拦截器
Interceptor interceptor = interceptors.get(index);
//调用该拦截器并获取返回结果
Response response = interceptor.intercept(next);
// Confirm that the next interceptor made its required call to chain.proceed().
if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
throw new IllegalStateException("network interceptor " + interceptor
+ " must call proceed() exactly once");
}
// Confirm that the intercepted response isn't null.
if (response == null) {
throw new NullPointerException("interceptor " + interceptor + " returned null");
}
return response;
}
总结
以上就是我们对 OkHttp 核心源码的分析。当我们发起一个请求的时候会初始化一个 Call 的实例,然后根据同步和异步的不同,分别调用它的 execute() 和 enqueue() 方法。大致过程都是通过拦截器组成的责任链,依次经过重试、桥接、缓存、连接和访问服务器等过程,来获取到一个响应并交给用户。
转载自:https://juejin.cn/post/6844903793243635719