okhttp分享一:okhttp基本使用及interceptor链
本文主要内容如下
- okhttp 简介
- okhttp 使用
- okhttp 请求构建基础类源码分析
一、okhttp简介
现在我们说的okhttp一般是指okhttp3.0之后的版本,okhttp3.0之后square公司对okhttp框架进行了重构,解决了很多2.x中存在的bug,引入了拦截器模式,支持了更多的http字段配置,并最终形成了现在的okhttp框架。本文所有的代码分析均基于okhttp 3.12.0版本,okhttp最近推出了okhttp4.0版本,用kotlin重写了一遍okhttp,但逻辑与最新的3.x保持一致。
我看到很多文章都会比较okhttp与volley,再讲解okhttp之前,我们先来简单理一下okhttp、volley、httpurlconnection这三个安卓侧使用最广的几个网络请求框架,先看示意图
实际上volley是在httpurlconnection的基础上进行了进一步的封装,加入了一些线程管理,缓存逻辑,其底层网络发送用根据版本不同(>2.2)分别使用了httpurlconnection、httpclient;而httpurlconnection在4.4后底层是使用okhttp2.x实现的,以下是android源码中集成的okhttp版本对应情况
- Android 4.4.4_r1 : okhttp 1.1.2
- Android 5.0.1_r1 : okhttp 2.0.0
- Android 6.0.1_r1 : okhttp 2.4.0
- Android 7.1.0_r1 : okhttp 2.6.0
可以看到,android7才集成了2.6.0的okhttp,因此虽然说httprulconnection底层使用的是okhttp,但并不是我们现在普遍说的okhttp(3.x)。在开始看下面的讲解前,大家最好可以看看http、rfc协议相关知识,okhttp基本就是http协议在android侧的实现与优化。
okhttp优点如下
- 支持spdy、http2、websocket
- 根据策略选择最优路线
- 有自动维护的链接池,从代码层面复用tcp链接,减少握手次数
- 支持同步与异步请求,通过线程池处理并发
- 责任链模式,模块功能明晰,扩展性好。
- 使用基于NIO的okio框架代替javaio,速度快
- 严格遵循http、rfc协议
二、okhttp使用
2.1、get
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(url)
.build();
// 同步
// Response response = client.newCall(request).execute();
// 异步
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
...
}
@Override
public void onResponse(Call call, Response response) throws IOException {
...
});
2.2、POST
post和get的不同在于对Request请求的构造不同(因为post需要携带参数),post方式中的Request需要传递一个RequestBody作为post的参数。RequestBody有两个子类:FormBody和MultipartBody。
这两个子类分别对应表单提交和文件上传,除此以外,requestBody还可以用json格式。以json格式为例,使用方式如下。
public static final MediaType JSON
= MediaType.parse("application/json; charset=utf-8");
OkHttpClient client = new OkHttpClient();
RequestBody body = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
// 同步
// Response response = client.newCall(request).execute();
// 异步
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
...
}
@Override
public void onResponse(Call call, Response response) throws IOException {
...
});
三、okhttp 请求构建基础类源码分析
根据从上面的GET请求,显示用builder构建了Request对象,然后执行了OKHttpClient.java的newCall方法,那么我们先下看这个newCall里面都做了什么
3.1 Call及RealCall
/**
* 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 {
/** Returns the original request that initiated this call. */
Request request();
/**
* Invokes the request immediately, and blocks until the response can be processed or is in
* error.
*
* To avoid leaking resources callers should close the {@link Response} which in turn will
* close the underlying {@link ResponseBody}.
*
*
{@code
*
* // ensure the response (and underlying response body) is closed
* try (Response response = client.newCall(request).execute()) {
* ...
* }
*
* }
*
* The caller may read the response body with the response's {@link Response#body} method. To
* avoid leaking resources callers must {@linkplain ResponseBody close the response body} or the
* Response.
*
*
Note that transport-layer success (receiving a HTTP response code, headers and body) does
* not necessarily indicate application-layer success: {@code response} may still indicate an
* unhappy HTTP response code like 404 or 500.
*
* @throws IOException if the request could not be executed due to cancellation, a connectivity
* problem or timeout. Because networks can fail during an exchange, it is possible that the
* remote server accepted the request before the failure.
* @throws IllegalStateException when the call has already been executed.
*/
Response execute() throws IOException;
/**
* Schedules the request to be executed at some point in the future.
*
*
The {@link OkHttpClient#dispatcher dispatcher} defines when the request will run: usually
* immediately unless there are several other requests currently being executed.
*
*
This client will later call back {@code responseCallback} with either an HTTP response or a
* failure exception.
*
* @throws IllegalStateException when the call has already been executed.
*/
void enqueue(Callback responseCallback);
/** Cancels the request, if possible. Requests that are already complete cannot be canceled. */
void cancel();
/**
* Returns true if this call has been either {@linkplain #execute() executed} or {@linkplain
* #enqueue(Callback) enqueued}. It is an error to execute a call more than once.
*/
boolean isExecuted();
boolean isCanceled();
/**
* Create a new, identical call to this one which can be enqueued or executed even if this call
* has already been.
*/
Call clone();
interface Factory {
Call newCall(Request request);
}
}
Call是一个接口类,从注释可以了解,Call可以理解为对一次请求的封装,需要对请求的操作,Call中都有对应方法。Call接口内部提供了Factory工厂方法模式(将对象的创建延迟到工厂类的子类去进行,从而实现动态配置)。
OKHttpClient实现了Call.Factory接口,返回了一个RealCall对象。那我们就来看下RealCall这个类。
final class RealCall implements Call {
final OkHttpClient client;
final RetryAndFollowUpInterceptor retryAndFollowUpInterceptor;
/**
* There is a cycle between the {@link Call} and {@link EventListener} that makes this awkward.
* This will be set after we create the call instance then create the event listener instance.
*/
private EventListener eventListener;
/**
* The application's original request unadulterated by redirects or auth headers.
*/
final Request originalRequest;
final boolean forWebSocket;
// Guarded by this.
private boolean executed;
private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
}
static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
// Safely publish the Call instance to the EventListener.
RealCall call = new RealCall(client, originalRequest, forWebSocket);
call.eventListener = client.eventListenerFactory().create(call);
return call;
}
@Override
public Request request() {
return originalRequest;
}
// 同步请求
@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);
// 构建interceptor链并发送请求获取响应
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);
}
}
private void captureCallStackTrace() {
Object callStackTrace = Platform.get().getStackTraceForCloseable("response.body().close()");
retryAndFollowUpInterceptor.setCallStackTrace(callStackTrace);
}
// 异步请求
@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));
}
@Override
public void cancel() {
retryAndFollowUpInterceptor.cancel();
}
@Override
public synchronized boolean isExecuted() {
return executed;
}
@Override
public boolean isCanceled() {
return retryAndFollowUpInterceptor.isCanceled();
}
@SuppressWarnings("CloneDoesntCallSuperClone")
// We are a final type & this saves clearing state.
@Override
public RealCall clone() {
return RealCall.newRealCall(client, originalRequest, forWebSocket);
}
StreamAllocation streamAllocation() {
return retryAndFollowUpInterceptor.streamAllocation();
}
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);
}
}
}
/**
* Returns a string that describes this call. Doesn't include a full URL as that might contain
* sensitive information.
*/
String toLoggableString() {
return (isCanceled() ? "canceled " : "")
+ (forWebSocket ? "web socket" : "call")
+ " to " + redactedUrl();
}
String redactedUrl() {
return originalRequest.url().redact();
}
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List 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);
}
}
首先看下execute()方法,该方法是同步请求用的方法,该方法首先获取client对象中的dispatcher对象并调用其executed(RealCall call)方法,之后调用getResponseWithInterceptorChain方法构建拦截器链,并通过拦截器链发送请求获取response。okhttp中dispatcher负责任务分发、线程管理,虽然okhttpclient提供了外部传入dispatcher的api,但是Dispatcher类为final,也不建议自定义dipatcher传入。下面我们看一下先简单看下Dispatcher类中同步请求相关部分。
/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque runningSyncCalls = new ArrayDeque<>();
...
/** Used by {@code Call#execute} to signal it is in-flight. */
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
...
/** Used by {@code Call#execute} to signal completion. */
void finished(RealCall call) {
finished(runningSyncCalls, call, false);
}
private void finished(Deque calls, T call, boolean promoteCalls) {
int runningCallsCount;
Runnable idleCallback;
synchronized (this) {
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
if (promoteCalls) promoteCalls();
runningCallsCount = runningCallsCount();
idleCallback = this.idleCallback;
}
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
}
调用的executed方法实际上只将传入的call加入了一个runningSyncCalls的双向队列,而finish()方法就是将该call从队列中移除。因为是同步请求,所以并没有什么任务分发,线程管理,这些在同步请求中应该由外部处理。
再看下RealCall中另一个比较重要的方法getResponseWithInterceptorChain
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List 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);
}
从代码中看,该方法首先构建了一个List
- RetryAndFollowUpInterceptor:重试拦截器
处理重试的一个拦截器,会去处理一些异常,只要不是致命的异常就会重新发起一次请求(把Request给下级),如果是致命的异常就会抛给上一级;
处理一些重定向等等,比如 3xx、307、407 - BridgeInterceptor:
对于请求,设置一些通用的请求头,Cookie、Connection、Content-Type、Content-Length等;
对于响应,如果返回的数据被压缩了,采用 ZipSource。处理Cookie; - CacheInterceptor:
缓存拦截器,负责缓存相关处理,根据http协议中对缓存的规定,结合okio维护缓存逻辑。 - ConnectInterceptor:
负责建立连接的拦截器,经过该interceptor后,客户端与服务器的链接就已经成功建立。 - CallServerInterceptor:
基于ConnectInterceptor建立的链接,给服务器写数据和读取返回数据;
再看一下chain.proceed方法
@Override public Response proceed(Request request) throws IOException {
return proceed(request, streamAllocation, httpCodec, connection);
}
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;
}
看到在proceed方面里面又new了一个RealInterceptorChain类的next对象,这个next对象和chain最大的区别就是index属性值不同chain是0而next是1,然后取interceptors下标为1的对象的interceptor。由从上文可知,如果没有开发者自定义的Interceptor时,首先调用的RetryAndFollowUpInterceptor,如果有开发者自己定义的interceptor则调用开发者interceptor。
取出interceptor后调用其intercept方法,而每个interceor的intercept方法中又会调用chain.proceed方法,依次循环。到最后一个CallServerInterceptor后中断递归,直接返回response给上一个interceptor,在一层一层处理response后向上返回,最后回到realCall中的execute方法。其流程如下图