hello,大家好,这篇文档将会分析当前流行的网络框架okhttp的源码流程
一、基本使用
// 获得okhttpclient对象
OkHttpClient client = new OkHttpClient();
// 初始化请求参数
Request request = new Request.Builder()
.get()
.url("http://www.baidu.com")
.build();
okhttp3.Call call = client.newCall(request);
// 执行请求操作
call.enqueue(new okhttp3.Callback() {
@Override
public void onFailure(okhttp3.Call call, IOException e) {
Log.d(TAG,"成功");
}
@Override
public void onResponse(okhttp3.Call call, okhttp3.Response response) throws IOException {
Log.d(TAG,"失败");
}
});
}
上诉简简单单的几行代码就完成了一次网络请求操作,很简单。接下来我们开始进行源码分析
二、源码分析
@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));
}
我们从请求操作的call.enqueue()方法开始分析,这个方法加了一个同步锁避免多线程的问题。我们再来看一下client.dispatcher()有什么作用。
public final class Dispatcher {
// 请求最大数量
private int maxRequests = 64;
// 同一目标主机同时访问最大数量
private int maxRequestsPerHost = 5;
private @Nullable Runnable idleCallback;
/** 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<>();
这个类是一个调度者,他的工作是初始化线程池和一些请求队列,接下来我们再来看一的Dispatcher.enqueue()方法
synchronized void enqueue(AsyncCall call) {
// 正在执行的异步请求是否小于64 && 正在执行的目标主机数量是否小于5
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
// 添加到正在执行请求的队列
runningAsyncCalls.add(call);
// 执行本次请求
executorService().execute(call);
} else {
// 添加到等待队列
readyAsyncCalls.add(call);
}
}
enqueue()是判断当前请求是否能直接执行还是添加到等待队列中去,好了到了这里简单的请求就完成了。哪它到底是在哪里真正的执行了请求的呢。接下来我们重新回到client.dispatcher().enqueue(new AsyncCall(responseCallback)); enqueue方法传了一个AsyncCall类,它到底是什么呢??接下来,我们来看一下这个类是干什么的
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);
}
}
}
它是RealCall的一个静态内部类,它的父类是NamedRunnable,接下来我们来看一下它的父类又是干什么的
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();
}
NamedRunnable是一个抽象的类,它实现了Runnable接口,还重写了run()方法并且执行了execute()方法,咦?这个方法不是有点眼熟,好像在哪见过。。。是的,AsyncCall类实现了execute()方法做了真正的实事情。接下来我们来看一看AsyncCall.execute()究竟做了什么
@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);
}
}
}
我们可以看到这方法里拿到了请求之后的返回参数,并且最终回调到了接口,这就代表我们一次完整的网络请求已经结束了。接下来我们看一下它的核心方法 Response response = 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);
}
getResponseWithInterceptorChain()是初始化了拦截器,并且交予RealInterceptorChain类进行处理
@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.
// index + 1 这里会每次+1
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
// 从拦截器数据中取出当前的拦截器
Interceptor interceptor = interceptors.get(index);
// 执行当前拦截器的intercept方法 链式调用
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;
}
这里会取出拦截器数组中的拦截器进行参数配置,并且调用每个拦截器中的intercept()方法进行当前拦截器的参数封装
@Override public Response intercept(Chain chain) throws IOException {
Request userRequest = chain.request();
Request.Builder requestBuilder = userRequest.newBuilder();
RequestBody body = userRequest.body();
if (body != null) {
MediaType contentType = body.contentType();
if (contentType != null) {
requestBuilder.header("Content-Type", contentType.toString());
}
long contentLength = body.contentLength();
if (contentLength != -1) {
requestBuilder.header("Content-Length", Long.toString(contentLength));
requestBuilder.removeHeader("Transfer-Encoding");
} else {
requestBuilder.header("Transfer-Encoding", "chunked");
requestBuilder.removeHeader("Content-Length");
}
}
if (userRequest.header("Host") == null) {
requestBuilder.header("Host", hostHeader(userRequest.url(), false));
}
if (userRequest.header("Connection") == null) {
requestBuilder.header("Connection", "Keep-Alive");
}
// If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing
// the transfer stream.
boolean transparentGzip = false;
if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
transparentGzip = true;
requestBuilder.header("Accept-Encoding", "gzip");
}
List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
if (!cookies.isEmpty()) {
requestBuilder.header("Cookie", cookieHeader(cookies));
}
if (userRequest.header("User-Agent") == null) {
requestBuilder.header("User-Agent", Version.userAgent());
}
Response networkResponse = chain.proceed(requestBuilder.build());
HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());
Response.Builder responseBuilder = networkResponse.newBuilder()
.request(userRequest);
if (transparentGzip
&& "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
&& HttpHeaders.hasBody(networkResponse)) {
GzipSource responseBody = new GzipSource(networkResponse.body().source());
Headers strippedHeaders = networkResponse.headers().newBuilder()
.removeAll("Content-Encoding")
.removeAll("Content-Length")
.build();
responseBuilder.headers(strippedHeaders);
String contentType = networkResponse.header("Content-Type");
responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody)));
}
return responseBuilder.build();
}
比如说BridgeInterceptor拦截器的http头参数拼接:Content-Type,Content-Length等。每个拦截器有自己不同的职责,最后拦截器还是会调用chain.proceed(requestBuilder.build());交予下一个拦截器处理自己的职责。当最后一个拦截器处理完之后,它会链式回调给每个拦截器。最终返回到RealCall类的getResponseWithInterceptorChain()方法,调用到回调接口。自此okhttp网络请求结束。
三、总结
总的来说okhttp的主线源码就是这样,其中还有很多细节文章没有介绍到,还需要大家自己分析。好了,这篇文档就到这里了,有什么说的不对的地方希望大家提出来。