Okhttp
一、基本用法
以post请求为例,基本用法如下:
OkHttpClient client = new OkHttpClient.Builder().build();
FormBody body = new FormBody.Builder()
.add("phone","1762192344")
.add("passwd","1ass21")
.build();
Request request = new Request
.Builder()
.post(body)
.url("http://xxxxxxxx/login")
.build();
Call call = client.newCall(request);
Response execute = null;
try {
//同步请求
execute = call.execute();
// 异步请求
// call.enqueue(new Callback() {
// @Override
// public void onFailure(Call call, IOException e) {
//
// }
//
// @Override
// public void onResponse(Call call, Response response) throws IOException {
//
// }
// });
Log.d(TAG, "okHttp3: "+execute.body().string());
} catch (IOException e) {
e.printStackTrace();
}
可以看到大致分四步:
1、构建OkHttpClient对象;
2、构建Request请求对象;
3、构建Call对象;
4、发起请求;
下面一步一步分析
二、4步逐步分析
1.构建OkHttpClient对象
OkHttpClient采用建造者模式创建对象,源码如下:
这就是一个标准的构建者模式,将http请求的一些配置封装到client对象中。
可以看到里面有分发器,拦截器,超时设置等。
2.构建Request请求对象
这也是一个建造者模式把请求的url、method、header全部封装到Request对象中。
3.创建Call对象
先来看看Call,这是一个接口,定义了一些request()、enqueue(Callback responseCallback)、execute()等方法,其实现类是RealCall,先搁置一边。回过头来看client.newCall(request)跟进代码,查看newCall(Request request)方法。
OkHttpClient类:
RealCall类:
果然,这里就只是跟进传进来的Request和当前的client对象创建了一个RealCall对象,也就是说使用方法中的第三步(Call call = client.newCall(request))执行完成后,得到了一个ReallCall对象,接下来到了第四步。
4. 发起请求
先看execute = call.execute()请求
a).同步请求execute()
关键代码为标出来的注释1和注释2两处,先看注释1,看下client.dispatcher()返回的Dispatcher对象:
这是一个调度器,内部维护着最大请求数,每个主机最大请求数等参数,最重要的是维护着三个队列,分别是“将要执行的异步请求队列”、“正在执行的异步请求队列”和“正在执行的同步执行队列”。之前的代码段中注释1处调用dispatcher.executed(this)方法,我们看到这个方法只是把当前的realCall实例加入到了正在执行的同步请求队列中。接下来看注释2处的代码Response result = getResponseWithInterceptorChain(),看下这个方法:
在这个方法中,将用户自定义的一些拦截器和默认的拦截器封装到一个list中,然后创建RealInterceptorChain对象并执行proceed(originalRequest)方法,接下来将是重点。看一下这个方法
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
//...省去异常处理...
// 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);
//...省去异常处理...
return response;
}
关键代码只有三行,这里会遍历调用拦截器列表中的拦截器,并调用每一个拦截器的intercept(RealInterceptorChain chain)方法,先看这里的第一个拦截器RetryAndFollowUpInterceptor的intercept(Chain chain)方法
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
RealInterceptorChain realChain = (RealInterceptorChain) chain;
//...省略部分代码....
int followUpCount = 0;
Response priorResponse = null;
while (true) {
//...省略异常处理代码...
Response response;
boolean releaseConnection = true;
try {
//注释3
response = realChain.proceed(request, streamAllocation, null, null);
releaseConnection = false;
} catch (RouteException e) {
// The attempt to connect via a route failed. The request will not have been sent.
if (!recover(e.getLastConnectException(), streamAllocation, false, request)) {
throw e.getFirstConnectException();
}
releaseConnection = false;
continue;
} catch (IOException e) {
// An attempt to communicate with a server failed. The request may have been sent.
boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
if (!recover(e, streamAllocation, requestSendStarted, request)) throw e;
releaseConnection = false;
continue;
} finally {
// We're throwing an unchecked exception. Release any resources.
if (releaseConnection) {
streamAllocation.streamFailed(null);
streamAllocation.release(true);
}
}
// Attach the prior response if it exists. Such responses never have a body.
if (priorResponse != null) {
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder()
.body(null)
.build())
.build();
}
Request followUp;
try {
followUp = followUpRequest(response, streamAllocation.route());
} catch (IOException e) {
streamAllocation.release(true);
throw e;
}
if (followUp == null) {
streamAllocation.release(true);
return response;
}
closeQuietly(response.body());
if (++followUpCount > MAX_FOLLOW_UPS) {
streamAllocation.release(true);
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}
if (followUp.body() instanceof UnrepeatableRequestBody) {
streamAllocation.release(true);
throw new HttpRetryException("Cannot retry streamed HTTP body", response.code());
}
if (!sameConnection(response, followUp.url())) {
streamAllocation.release(false);
streamAllocation = new StreamAllocation(client.connectionPool(),
createAddress(followUp.url()), call, eventListener, callStackTrace);
this.streamAllocation = streamAllocation;
} else if (streamAllocation.codec() != null) {
throw new IllegalStateException("Closing the body of " + response
+ " didn't close its backing stream. Bad interceptor?");
}
request = followUp;
priorResponse = response;
}
}
在一个while(true)中执行,当重连次数followUpCount超过20次的时候,停止重连。当执行到注释3的时候,又会重新执行RealInterceptorChain.proceed()方法。但是这次会取出下一个拦截器BridgeInterceptor并执行它的intercept(Chain chain)方法。以这种方式就会遍历完拦截器list中的拦截器,并调用响应的intercept方法,接下来的几个拦截器的intercept方法比较简单,就不分析了。同样也会在此方法中调用realChain.proceed()方法进入下一个拦截器,直到最后一个拦截器CallServerInterceptor,看下它的intercept方法:
@Override public Response intercept(Chain chain) throws IOException {
final RealInterceptorChain realChain = (RealInterceptorChain) chain;
//...省略代码
realChain.eventListener().requestHeadersStart(call);
//向服务端发起请求
httpCodec.writeRequestHeaders(request);
realChain.eventListener().requestHeadersEnd(call, request);
Response.Builder responseBuilder = null;
//如果Request请求的body不为空,需要包装RequestBody并发送
if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
//...省略代码....
if (responseBuilder == null) {
if (request.body() instanceof DuplexRequestBody) {
//...省略代码....
} else {
// Write the request body if the "Expect: 100-continue" expectation was met.
realChain.eventListener().requestBodyStart(call);
//获取body内容的长度
long contentLength = request.body().contentLength();
CountingSink requestBodyOut =
new CountingSink(httpCodec.createRequestBody(request, contentLength));
BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
//发送RequestBody
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
realChain.eventListener().requestBodyEnd(call, requestBodyOut.successfulCount);
}
} else if (!connection.isMultiplexed()) {
// ...省略代码....
}
}
//完成请求
if (!(request.body() instanceof DuplexRequestBody)) {
httpCodec.finishRequest();
}
if (responseBuilder == null) {
realChain.eventListener().responseHeadersStart(call);
//读取返回的Response
responseBuilder = httpCodec.readResponseHeaders(false);
}
//构建Response
responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis());
Internal.instance.initCodec(responseBuilder, httpCodec);
Response response = responseBuilder.build();
//...省略了Response的code==100的处理...
if (forWebSocket && code == 101) {
// Connection is upgrading, but we need to ensure interceptors see a non-null response body.
// 构建一个非空的Response返回
} else {
response = response.newBuilder()
.body(httpCodec.openResponseBody(response))
.build();
}
//...关闭资源...
//...异常处理...
return response;
}
下面详细分析一下这个方法:分析这个方法之前我们先看HttpCodec这个接口的介绍Encodes HTTP requests and decodes HTTP responses。编码http请求并解码http返回结果,也就是说真正的处理请求和结果的是这个接口,它有两个实现类分别是Http1Codec和Http2Codec分别对应Http/1.x和Http/2.x。
好了,开始继续之前的分析,首先通过httpCodec.writeRequestHeaders(request)发起请求,以Http1Codec为例
/**
* Prepares the HTTP headers and sends them to the server.
*
* <p>For streaming requests with a body, headers must be prepared <strong>before</strong> the
* output stream has been written to. Otherwise the body would need to be buffered!
*
* <p>For non-streaming requests with a body, headers must be prepared <strong>after</strong> the
* output stream has been written to and closed. This ensures that the {@code Content-Length}
* header field receives the proper value.
*/
@Override public void writeRequestHeaders(Request request) throws IOException {
String requestLine = RequestLine.get(
request, streamAllocation.connection().route().proxy().type());
writeRequest(request.headers(), requestLine);
}
RealBufferedSink
看javadoc写明了Prepares the HTTP headers and sends them to the server.,读取请求行并调用writeRequest(request.headers(), requestLine)方法
/** Returns bytes of a request header for sending on an HTTP transport. */
public void writeRequest(Headers headers, String requestLine) throws IOException {
if (state != STATE_IDLE) throw new IllegalStateException("state: " + state);
sink.writeUtf8(requestLine).writeUtf8("\r\n");
for (int i = 0, size = headers.size(); i < size; i++) {
sink.writeUtf8(headers.name(i))
.writeUtf8(": ")
.writeUtf8(headers.value(i))
.writeUtf8("\r\n");
}
sink.writeUtf8("\r\n");
state = STATE_OPEN_REQUEST_BODY;
}
发现oKHttp通过okio的Sink对象(该对象可以看做Socket的OutputStream对象)的writeRequest来向服务器发送请求的(如想了解更多,参靠okio源码分析);
继续之前的分析,发送完header之后,如果请求有请求body,还需要发送请求bodyhttpCodec.createRequestBody(request, contentLength)创建了固定长度http bodyFixedLengthSink然后将请求body通过Okio.buffer()方法封装成RealBufferedSink对象,最后通过RequestBody的writeTo方法.
这里是RequestBody的子类FormBody的writeTo,写入了okio的Buffer中,请求完成调用httpCodec.finishRequest();,跟进内部发现调用了flush()方法,把‘流’都写到请求中,请求完成。但是服务器结果还没返回 ,我们继续。读取返回结果的代码,responseBuilder = httpCodec.readResponseHeaders(false);这段代码的解析来自一个http返回的header,
@Override
public Response.Builder readResponseHeaders(boolean expectContinue) throws IOException {
//...省略代码....
try {
StatusLine statusLine = StatusLine.parse(readHeaderLine());
Response.Builder responseBuilder = new Response.Builder()
.protocol(statusLine.protocol)
.code(statusLine.code)
.message(statusLine.message)
.headers(readHeaders());
return responseBuilder;
} catch (EOFException e) {
}
}
解析返回结果的header并构建Response.Builder对象,至于请求体在哪?接着往下看,拿到Response.Builder对象后就可以构建Response对象,然后通过httpCodec.openResponseBody(response)获取请求体,结合刚才获取Response生成一个新的Response返回。
至此整个流程就分析完了,但是这只是call.execute()同步执行的流程,那异步执行的流程是怎么样的呢?继续往下看
b).异步请求enqueue(Callback responseCallback)
与同步请求不同的是,异步请求执行的是RealCall的enqueue(Callback responseCallback)方法:
@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));
}
可以看到这个方法最终生成了一个AsyncCall对象并执行了dispatcher的enqueue(AsyncCall call)接下来看下这个方法
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();
}
前面我们说过Dispatcher维护着三个队列,两个异步的和一个同步的,此方法将异步请求加入到即将执行的异步队列中,然后开始执行
/**
* Promotes eligible calls from {@link #readyAsyncCalls} to {@link #runningAsyncCalls} and runs
* them on the executor service. Must not be called with synchronization because executing calls
* can call into user code.
*
* @return true if the dispatcher is currently running calls.
*/
private boolean promoteAndExecute() {
assert (!Thread.holdsLock(this));
List<AsyncCall> executableCalls = new ArrayList<>();
boolean isRunning;
synchronized (this) {
//遍历将要执行的异步请求队列
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall asyncCall = i.next();
//正在执行异步请求队列已满,跳出循环
if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; // Host max capacity.
//从当前队列移除
i.remove();
asyncCall.callsPerHost().incrementAndGet();
executableCalls.add(asyncCall);
//将当前异步请求加进正在执行的队列中
runningAsyncCalls.add(asyncCall);
}
isRunning = runningCallsCount() > 0;
}
for (int i = 0, size = executableCalls.size(); i < size; i++) {
AsyncCall asyncCall = executableCalls.get(i);
asyncCall.executeOn(executorService());
}
return isRunning;
}
将准备队列中的请求取出来,如果满足条件就放到正在执行队列中,并调用这些请求的executeOn(executorService())方法,executorService()返回线程池,所以看executeOn()方法
/**
* Attempt to enqueue this async call on {@code executorService}. This will attempt to clean up
* if the executor has been shut down by reporting the call as failed.
*/
void executeOn(ExecutorService executorService) {
assert (!Thread.holdsLock(client.dispatcher()));
boolean success = false;
try {
executorService.execute(this);
success = true;
} catch (RejectedExecutionException e) {
InterruptedIOException ioException = new InterruptedIOException("executor rejected");
ioException.initCause(e);
eventListener.callFailed(RealCall.this, ioException);
responseCallback.onFailure(RealCall.this, ioException);
} finally {
if (!success) {
client.dispatcher().finished(this); // This call is no longer running!
}
}
}
调用线程池的execute(this)执行当前的call,若捕获异常就调用responseCallback.onFailure(RealCall.this, ioException);,先看一下AsyncCall这个类是什么,此类继承自NamedRunnable,而NamedRunnable是实现了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();
}
当此线程被执行的时候,会调用execute()方法,也就会调用到子类AsyncCall的execute()方法,来看一下这个方法中做了什么
@Override
protected void execute() {
boolean signalledCallback = false;
timeout.enter();
try {
//注释4
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) {
e = timeoutExit(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);
}
}
}
看注释4,看到了我们之前分析过的代码,之后的流程就跟同步请求的顺序执器拦截器的代码逻辑一样了。至此okhttp3的同步和异步请求都执行完了。
总结
简单总结一下,okhttp3通过建造者模式构建OkHttpClient及Request,通过线程池执行异步请求,通过拦一系列截器与服务器建立连接、发送数据并解析返回结果。
用流程图表示如下: