OkHttp 是一款用于 Android 和 Java 的网络请求库,也是目前 Android 中最火的一个网络库。OkHttp 有不少的优势:java
以前写过一篇 Retrofit 源码解析,Retrofit 底层其实就是用的 OkHttp 去请求网络。本文分析 OKHttp 的源码,主要是针对一次网络请求的基本流程,源码基于 OKHttp-3.8.0web
下面是 OkHttp 的使用示例:segmentfault
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 { } });
首先是建立一个 OkHttpClient
对象,其实用过的应该知道能够用 new OkHttpClient.Builder().build()
的方式来配置 OkHttpClient
的一些参数。有了 OkHttpClient
以后,下面是建立一个 Request
对象,这个对象也是经过 Builder 模式来生成,其中能够配置一些与这条请求相关的参数,其中 url 是必不可少的。在发送请求的时候,须要生成一个 Call
对象,Call
表明了一个即将被执行的请求。若是是同步请求,调用 execute
方法。异步则调用 enqueue
,并设定一个回调对象 Callback
。缓存
下面就一步步分析发送一条网络请求的基本流程。cookie
OkHttpClient 的建立采用了 Builder 模式,能够配置 Interceptor、Cache 等。能够设置的参数不少,其中部分参数以下:网络
final Dispatcher dispatcher; // 请求的分发器 final @Nullable Proxy proxy; // 代理 final List<Protocol> protocols; // http协议 final List<ConnectionSpec> connectionSpecs; final List<Interceptor> interceptors; final List<Interceptor> networkInterceptors; final EventListener.Factory eventListenerFactory; final ProxySelector proxySelector; final CookieJar cookieJar; final @Nullable Cache cache;
Request 与 OkHttpClient 的建立相似,也是用了 Buidler 模式,可是其参数要少不少:并发
public final class Request { final HttpUrl url; final String method; final Headers headers; final @Nullable RequestBody body; final Object tag; ... }
参数的含义都很明确,即便 Http 协议的url、header、method 以及 body 部分。变量 tag
用于标识一条 Request
,可用于发送后取消这条请求。异步
client.newCall(request)
生成一个 Call 对象。Call 其实是一个接口,它封装了 Request,而且用于发起实际的网络请求。下面是 Call 的所有代码:socket
public interface Call extends Cloneable { Request request(); Response execute() throws IOException; void enqueue(Callback responseCallback); void cancel(); boolean isExecuted(); boolean isCanceled(); Call clone(); interface Factory { Call newCall(Request request); } }
其中包含了与网络请求相关的操做,包括发起、取消等。看一下 OkHttpClient
是如何建立 Call
的:async
@Override public Call newCall(Request request) { return new RealCall(this, request, false /* for web socket */); }
从代码能够看到,其实是建立了一个 RealCall
对象,它也是 Call 的惟一一个实现类。
有了 RealCall
对象后,就能够发起网络请求了,能够是同步请求(execute
)或者是异步请求(enqueue
)。异步请求涉及到 Dispatcher
,先从相对简单的同步请求开始分析。
调用 RealCall#execute()
便是发起同步请求,代码以下:
@Override public Response execute() throws IOException { synchronized (this) { if (executed) throw new IllegalStateException("Already Executed"); executed = true; } captureCallStackTrace(); try { client.dispatcher().executed(this); Response result = getResponseWithInterceptorChain(); if (result == null) throw new IOException("Canceled"); return result; } finally { client.dispatcher().finished(this); } }
首先判断这条请求是否是已经执行过,若是是则会抛出异常(一条请求只能执行一次,重复执行能够调用 Call#clone()
)。接着执行了 client.dispatcher().executed(this)
,这行代码是把当前的 Call 加入到 Dispatcher 的一个队列中,这个暂时能够忽略,后面会分析 Dispatcher。
下面一行 Response result = getResponseWithInterceptorChain()
是关键,在 getResponseWithInterceptorChain
中真正执行了网络请求并得到 Response 并返回。(下一小节具体分析其中的逻辑)
最后在 finally 中调用 Dispatcher 的 finished
,从队列中移除这条请求。
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); return chain.proceed(originalRequest); }
能够看到,其中建立了一个 List 用于添加 Interceptor。首先添加的是 client 中的 interceptors,也就是在建立 OkHttpClient
对象时自定义的 interceptors,而后依次添加 retryAndFollowUpInterceptor
(重试及重定向)、BridgeInterceptor
(请求参数的添加)、CacheInterceptor
(缓存)、ConnectInterceptor
(开始链接)、用户自定义的 networkinterceptors
及 CallServerInterceptor
(发送参数并读取响应)。从这里能够知道,OkHttp 默认添加了好几个 interceptor 用于完成不一样的功能。
在研究各个 interceptor 以前,须要考虑一下如何让这些拦截器一个接着一个的执行?继续看上面的代码,在添加了各类 interceptors 以后,建立了一个 RealInterceptorChain
对象。(它的构造函数须要的参数不少,而且这些参数涉及到链接池、请求数据的发送等。因为这篇文章主要分析 OkHttp 的基本流程,因此暂时略过这部分)RealInterceptorChain
是接口 Chain
的实现类,Chain
是 链 的意思,其做用是把各个 Interceptor 串起来依次执行。在得到了 RealInterceptorChain
以后调用其 proceed
方法,看名字就能知道是让 Request
请求继续执行。
下面具体分析 RealInterceptorChain
,它有以下的成员变量:
private final List<Interceptor> interceptors; // 拦截器 private final StreamAllocation streamAllocation; // 流管理器 private final HttpCodec httpCodec; // http流,发送请求数据并读取响应数据 private final RealConnection connection; // scoket的链接 private final int index; // 当前拦截器的索引 private final Request request; // 当前的请求 private int calls; // chain 的 proceed 调用次数的记录
其中 streamAllocation
、httpCodec
和 connection
都与 socket 链接有关,后续文章再分析。看一下 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. // 若是已经有了一个流,确保即将到来的 request 是用它 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(). // 若是已经有了一个流,确保这是对 call 惟一的调用 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); // (1) Interceptor interceptor = interceptors.get(index); // (2) Response response = interceptor.intercept(next); // (3) // 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; }
刚开始作了一些链接方面的判断,须要关注的是标了(1)、(2)、(3)的几行,主要作了如下操做:
RealInterceptorChain
,其中 index
加1用于标识当前的拦截器index
获取当前的拦截器intercept
方法,并把上面生成的新的 RealInterceptorChain 对象 next
传进去由以前的 getResponseWithInterceptorChain
方法能够知道,当前 RealInterceptorChain
的 interceptors 的第一个是 RetryAndFollowUpInterceptor
,下面是其 intercept
的代码:
@Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); streamAllocation = new StreamAllocation( client.connectionPool(), createAddress(request.url()), callStackTrace); int followUpCount = 0; Response priorResponse = null; while (true) { if (canceled) { streamAllocation.release(); throw new IOException("Canceled"); } Response response = null; boolean releaseConnection = true; try { // 调用 chain 的 proceed response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null); releaseConnection = false; } catch (RouteException e) { ... // 省略部分代码,主要是错误重试以及重定向 } }
这个 Interceptor 主要用于出错重试以及重定向的逻辑,其中省略了部分代码。在这个方法当中要关注的是再次调用了 chain
的 proceed
方法,这里的 chain
是以前新建立的 next
对象。至关于说经过调用 Chain#proceed()
将网络请求推向下一个拦截器(proceed
中会获取下一个 Interceptor 并调用其 intercept
方法),而且获得 response 对象,而下一个拦截器也是相似的操做。因而,多个 interceptors 就经过这种方式串起来依次执行,而且前一个 Interceptor 能够获得后一个 Interceptor 执行后的 response 从而进行处理。
经过不一样的 Interceptor,OkHttp 实现了不一样的功能。各个 Inercept 职责分明又不会互相耦合,而且能够很是方便的添加 Interceptor,这是 责任链 模式的体现,很是优雅的设计。如今能够发现 OkHttp 中的拦截器的调用过程以下图所示:
相比于同步请求,异步请求主要是增长了 Dispatcher 的处理。Dispatcher 是请求的分发器,它有一下的成员变量:
private int maxRequests = 64; // 最大链接数 private int maxRequestsPerHost = 5; // 单个 host 最大链接数 private @Nullable Runnable idleCallback; // 空闲时的回调 /** Executes calls. Created lazily. */ private @Nullable ExecutorService executorService; // 线程池 /** Ready async calls in the order they'll be run. */ // 准备执行的异步 Call 的队列 private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>(); /** Running asynchronous calls. Includes canceled calls that haven't finished yet. */ // 正在执行的的异步 Call 的队列 private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>(); /** Running synchronous calls. Includes canceled calls that haven't finished yet. */ // 正在执行的同步 Call 的队列 private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
在 Dispatcher 中,默认支持的最大并发链接数是64,每一个 host 最多能够有5个并发请求。
下面看一下线程池 executorService
的建立。线程池会在两个地方建立,分别是 Dispatcher 的构造函数或者是 executorService
方法中(若是调用了默认的构造函数):
// 默认构造函数没有建立 public Dispatcher() { } // 自定义线程池 public Dispatcher(ExecutorService executorService) { this.executorService = executorService; } // 若是没有自定义线程池,则默认建立 public synchronized ExecutorService executorService() { if (executorService == null) { executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false)); } return executorService; }
Dispatcher 支持自定义的线程池,不然会默认建立一个。在生成 OkHttpClient
对象时,默认调用的是 Dispatcher 无参的构造方法。这个默认线程池经过 `new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false))` 建立,看上去相似于一个 CachedThreadPool,没有常驻的 core 线程,空闲线程60秒后自动关闭。
每一个 Call 被添加到某一个队列,若是是同步请求添加到 runningSyncCalls
中:
synchronized void executed(RealCall call) { runningSyncCalls.add(call); }
异步请求添加的逻辑以下:
synchronized void enqueue(AsyncCall call) { if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) { runningAsyncCalls.add(call); executorService().execute(call); } else { readyAsyncCalls.add(call); } }
具体步骤是:
runningAsyncCalls
而且提交到线程池执行readyAsyncCalls
等待后续执行须要注意的是异步请求的 Call 不是原始的 Call,而是被包装为 AsyncCall
:
final class AsyncCall extends NamedRunnable { private final Callback responseCallback; AsyncCall(Callback responseCallback) { super("OkHttp %s", redactedUrl()); this.responseCallback = responseCallback; } ... @Override protected void execute() { boolean signalledCallback = false; try { // 调用 getResponseWithInterceptorChain 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 { responseCallback.onFailure(RealCall.this, e); } } finally { client.dispatcher().finished(this); } } }
AsyncCall
继承自 NamedRunnable
,它其实就是一个为线程设置了名字的 Runnable,在其 Run 中调用 execute
,因此 AsyncCall
的主要逻辑都写在 execute
中。能够看到最终仍是调用了 getResponseWithInterceptorChain
方法,因此后续执行网络请求的逻辑是同样的。在得到 response 以后,就能够调用 responseCallback
返回最终的信息。
在上面的代码中,finally 里面执行了 client.dispatcher().finished(this)
,在同步请求 RealCall#execute()
中也有相似的一行代码。finished
的做用是让 Dispatcher 从队列中移除已完成的 Call,对于异步请求还会从 readyAsyncCalls
中取出等待中的请求提交给线程池。下面是具体代码:
/** Used by {@code AsyncCall#run} to signal completion. */ void finished(AsyncCall call) { finished(runningAsyncCalls, call, true); } /** Used by {@code Call#execute} to signal completion. */ void finished(RealCall call) { finished(runningSyncCalls, call, false); } private <T> void finished(Deque<T> 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(); } } private void promoteCalls() { if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity. if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote. for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) { AsyncCall call = i.next(); // 找到一个等待队列中的 Call,符合链接数要求时加入 runningAsyncCalls 并提交给线程池执行。 if (runningCallsForHost(call) < maxRequestsPerHost) { i.remove(); runningAsyncCalls.add(call); executorService().execute(call); } if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity. } }
有两个重载的 finished
方法均调用了另外一个 pirvate 的 finished
,区别在于这个 finished
的最后一个参数 promoteCalls
。对于同步请求(参数为 RealCall
) promoteCalls
为 false
,而异步请求(参数为 AsyncCall
) promoteCalls
为 true
。 pirvate 的 finished
主要是从队列中移除 Call,异步请求会执行 promoteCalls
。promoteCalls
里面主要是从 readyAsyncCalls
取出一个 Call,若是知足最大链接数的要求,则把这个 Call 加入 runningAsyncCalls
并提交给线程池执行。
经过 runningAsyncCalls
和 readyAsyncCalls
,Dispatcher 实现了异步请求的调度执行。这里比较巧妙的方式是在 finally 中去执行 readyAsyncCalls
中的请求,避免了 wait/notity 的方式,避免了代码的复杂性。
OkHttp 的基本执行流程以下图所示:
主要是如下步骤:
OkHttpClient
调用 newCall
建立 RealCall
对象,Call
封装了 Request
,表明一条即将执行的请求。RealCall
的 execute
或 enqueue
方法,将Call
加入 Dispatcher
的相应队列中。最终,同步或异步请求都会调用 getResponseWithInterceptorChain
。getResponseWithInterceptorChain
中,OkHttp 添加用户自定义以及默认的 inceptors,并用一个 Chain
管理并依次执行每一个 Interceptor。Chain#proceed()
将请求发送给下一级的 Inceptor,并能经过这个方法得到下一级 Interceptor 的 Response。因此上图所示,Request 一级级地往下传递,而获取了网络的 Response 以后一级级地往上传递。OkHttp中一条网络请求的基本流程就是这样,下一篇文章介绍 OkHttp 如何创建链接:OkHttp 源码解析(二):创建链接。
若是个人文章对您有帮助,不妨点个赞支持一下(^_^)