okhttp在Android开发中已经很广泛了,所以理解其内部机制是很有必要的。
首先看一下最基本的okhttp的请求流程
GET请求:
OkHttpClient client = new OkHttpClient();
String run(String url) throws IOException {
Request request = new Request.Builder()
.url(url)
.build();
Response response = client.newCall(request).execute();
return response.body().string();
}
- 创建OkHttpClient的对象。我们可直接new出来 也可以通过OkHttpClient.Builder()构建。
- 创建Request对象。
- 通过OkHttpClient创建一个请求的任务Call。
- 同步请求直接执行任务 call.execute(),返回结果Response
- 异步请求执行call.enqueue(new Callback()…) 在callback回调中接收返回的结果Response
POST请求:
public static final MediaType JSON
= MediaType.parse("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();
Response response = client.newCall(request).execute();
return response.body().string();
}
- 创建OkHttpClient的对象。我们可直接new出来 也可以通过OkHttpClient.Builder()构建。
- 通过指定返回类型和post的参数创建RequestBody
- 创建Request对象。
- 通过OkHttpClient创建一个请求的任务Call。
- 同步请求直接执行任务 call.execute(),返回结果Response
- 异步请求执行call.enqueue(new Callback()…) 在callback回调中接收返回的结果Response
通过上面的基础用法,我们可以看到这几个对象:OkHttpClient,Request,Call,Response ,call.execute(),call.enqueue(new Callback()…) ,下面一一分析。
OkHttpClient
这个类代码看着有差不多一千行,其实大部分代码只要是初始化了后面需要用到的各种对象,如分发器dispatcher,各种拦截器,连接池connectionPool,缓存cache等等。
内部使用了构建者模式通过Builder创建各种对象。
最主要的一个方法就是newCall(Request request)返回一个Call对象。
Request
封装一个请求
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;
}
通过构建者模式初始化,内部封装了请求需要的url,请求类型,参数,请求头,请求体。
请求体RequestBody是一个抽象类它有两个实现类 FormBody和MultipartBody,这两个分别对应了我们post请求的时候的表单提交和文件提交。
Call
上面的例子中,创建完Request请求之后,通过OkHttpClient的newCall创建了一个Call对象。Call是什么呢?
Call是一个接口,是okhttp的核心类之一,我们通过它去执行网络请求。内部有个工厂接口将对象的创建延迟到子类中去创建。它的实现类就是RealCall。
我们也可以从OkHttpClient中的创建中看到,最后返回了RealCall对象。
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false );
}
- RealCall创建的时候,传了3个参数,一个是OkHttpClient,一个是Request,第三个用的少标记是不是webSokcet。所以RealCall可以方便的使用OkHttpClient和Request
- RealCall中有两个重要的方法execute() 和enqueue(Callback responseCallback) 分别对应了同步请求和异步请求。
- RealCall中还有一个很重要的方法getResponseWithInterceptorChain()。给请求添加拦截器。这个是okhttp的设计上的一个很灵活的地方,可以给请求添加自己想要的各种拦截器。系统自带的拦截器有重试重定向拦截器RetryAndFollowUpInterceptor,设置编码方式,添加头部信息,链接复用等拦截器BridgeInterceptor,缓存拦截器CacheInterceptor,网络链接拦截器ConnectInterceptor,发起网络请求接收网络响应拦截器CallServerInterceptor。这些拦截器都很重要,以后再一一分析。
下面主要先看一下execute() 和enqueue(Callback responseCallback) 同步和异步方法:
最开始的例子中client.newCall(request).execute()最终调用的就是RealCall中的execute()方法
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
try {
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
eventListener.callFailed(this, e);
throw e;
} finally {
client.dispatcher().finished(this);
}
}
首先第一个判断
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
判断这个call是否被执行过,执行过就抛异常了。说明一个call只能执行一次。
然后执行captureCallStackTrace()方法 从方法名字可以看出 是捕获call的堆栈跟踪信息
private void captureCallStackTrace() {
Object callStackTrace = Platform.get().getStackTraceForCloseable("response.body().close()");
retryAndFollowUpInterceptor.setCallStackTrace(callStackTrace);
}
在之后进入到核心类 Dispatcher中的client.dispatcher().executed(this)方法了。
Dispatcher这个对象在OkHttpClient初始化的时候就创建了。
下面看一下Dispatcher中的executed方法
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
首先synchronized 加锁了,是线程安全的。把call放入runningSyncCalls中
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
可以看到runningSyncCalls是一个双向队列。所以executed()方法就是把一个call放入到队列中。
然后就走到Response result = getResponseWithInterceptorChain()方法了
可以看到这个方法就返回了一个响应Response 。
而这个getResponseWithInterceptorChain()方法做了什么呢?
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
//添加开发者应用层自定义的Interceptor
interceptors.addAll(client.interceptors());
//这个Interceptor是处理请求失败的重试,重定向
interceptors.add(retryAndFollowUpInterceptor);
//这个Interceptor工作是添加一些请求的头部或其他信息
//并对返回的Response做一些友好的处理
interceptors.add(new BridgeInterceptor(client.cookieJar()));
//这个Interceptor的职责是判断缓存是否存在,读取缓存,更新缓存等等
interceptors.add(new CacheInterceptor(client.internalCache()));
//这个Interceptor的职责是建立客户端和服务器的连接
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);
}
可以看到里面创建了一个ArrayList,不断往里面添加各种拦截器。最后通过这些拦截器创建一个Interceptor.Chain对象(拦截器的链)最后调用了chain.proceed()方法。
看一下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");
}
// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
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");
}
if (response.body() == null) {
throw new IllegalStateException(
"interceptor " + interceptor + " returned a response with no body");
}
return response;
}
可以看到经过一系列的判断之后,又创建了一个名字叫next的RealInterceptorChain 对象。这个对象跟chain唯一的不同就是index加一了。其实这其中的意思就是遍历执行各个拦截器的proceed方法。直到执行到最后一个拦CallServerInterceptor,CallServerInterceptor中就不往下执行了,开始封装响应参数。
封装完后再依次往前传递,最后给第一个拦截器。最后执行到executed()方法中的 Response result = getResponseWithInterceptorChain(),最后返回响应的Response 给前端。
executed()方法的finally中,无论执行成功与否都回收掉所有的资源。
finally {
client.dispatcher().finished(this);
}
Response
封装一个返回响应
Response(Builder builder) {
this.request = builder.request;
this.protocol = builder.protocol;
this.code = builder.code;
this.message = builder.message;
this.handshake = builder.handshake;
this.headers = builder.headers.build();
this.body = builder.body;
this.networkResponse = builder.networkResponse;
this.cacheResponse = builder.cacheResponse;
this.priorResponse = builder.priorResponse;
this.sentRequestAtMillis = builder.sentRequestAtMillis;
this.receivedResponseAtMillis = builder.receivedResponseAtMillis;
}
也是通过构建者模式创建,内部封装了响应头,响应体,响应码,协议等。
响应体ResponseBody也是一个抽象类,它有两个实现CacheResponseBody,RealResponseBody分别代表了缓存的响应和真实的响应。
上面主要分析了同步的请求,下面看一下异步的请求,同步了解了,异步就好点了。异步的代码如下:这里要注意一点enqueue中的onFailure和onResponse方法都是在子线程中执行的,如果想操作界面还是得回到主线程
OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
Request request = new Request.Builder().url("").build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
enqueue方法:
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
最终还是执行到了Dispatcher中的enqueue方法传入了一个AsyncCall。看一下Dispatcher中的enqueue方法:
synchronized void enqueue(AsyncCall call) {
//如果正在执行的请求小于设定值即64,并且请求同一个主机的request小于设定值即5
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
//添加到执行队列,开始执行请求
runningAsyncCalls.add(call);
//获得当前线程池,没有则创建一个
executorService().execute(call);
} else {
//添加到等待队列中
readyAsyncCalls.add(call);
}
}
这里我们看到runningAsyncCalls和readyAsyncCalls 这个是跟上面的同步请求中的runningSyncCalls一块定义的都是双向队列
/** 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<>();
异步请求有两个队列,一个是等待队列readyAsyncCalls 。一个是正在执行的队列runningAsyncCalls 。正在执行的队列有上线 由参数maxRequests和maxRequestsPerHost 控制,正在执行的请求小于64个,并且请求同一个主机小于5的时候才往这个队列中添加这个call,并放到线程池中去执行,否则就放到等待的队列中去。
从上面我们看到传入enqueue的参数是一个AsyncCall ,AsyncCall 继承了NamedRunnable,NamedRunnable实现了 Runnable。所以AsyncCall 本质上就是一个Runnable。
public abstract class NamedRunnable implements Runnable {
protected final String name;
public NamedRunnable(String format, Object... args) {
this.name = Util.format(format, args);
}
@Override public final void run() {
String oldName = Thread.currentThread().getName();
Thread.currentThread().setName(name);
try {
execute();
} finally {
Thread.currentThread().setName(oldName);
}
}
protected abstract void execute();
}
final class AsyncCall extends NamedRunnable {
private final Callback responseCallback;
AsyncCall(Callback responseCallback) {
super("OkHttp %s", redactedUrl());
this.responseCallback = responseCallback;
}
String host() {
return originalRequest.url().host();
}
Request request() {
return originalRequest;
}
RealCall get() {
return RealCall.this;
}
@Override protected void execute() {
boolean signalledCallback = false;
try {
Response response = getResponseWithInterceptorChain();
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
signalledCallback = true;
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 {
eventListener.callFailed(RealCall.this, e);
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
}
所以这个AsyncCall 在线程池中最终也会执行到它的run方法。run方法里面最终执行到了execute()方法。这个execute()中执行的就跟我们同步请求的execute()基本一样了。
OK到这里okhttp的同步和异步的请求流程就通了。