iOkHttp的精髓是拦截器interceptor
.png)
最简单的okhttp实现
1 | String url = "http://wwww.baidu.com"; |
new OkhttpClient()过程
在new OkHttpClient()内部使用构造器模式初始化了一些配置信息:
- 支持协议,
- 任务分发器(其内部包含一个线程池,执行异步请求),
- 连接池(其内部包含一个线程池,维护connection),
- 连接/读/写超时时长等信息。
内部构造器如下:
1 | public OkHttpClient() { |
而构造器内部是:
1 | public Builder() { |
Dispatcher任务调度器
如代码所示,Bulider第一步是新建了一个Dispatcher任务调度器
Dispatcher代码为:
1 | public final class Dispatcher { |
Dispatcher任务调度器,它定义了
- 三个双向任务队列,
- 两个异步队列:准备执行的请求队列
readyAsyncCalls,正在运行的请求队列runningAsyncCalls; - 一个正在运行的同步请求队列
runningSyncCalls;
- 两个异步队列:准备执行的请求队列
- 另外还有一个线程池
executorService,这个线程池跟安卓中的CachedThreadPool非常类似,这种类型的线程池,适用于大量的耗时较短的异步任务。
Request.Builder()过程
接下来接着看要求的构造,这个例子请求比较简单,指定请求方式GET后请求url
1 | public static class Builder { |
okHttpClient.newCall(request)过程
接下来是:
1 | Call call = okHttpClient.newCall(request); |
拿到上一步传来的request,okHttpClient调用newCall()方法,创建处call对象,它的实现是RealCall
1 | public Call newCall(Request request) { |
可以看到在RealCall的构造方法中
- 创建了一个
RetryAndFollowUpInterceptor,用于处理请求错误和重定向等,这是Okhttp框架的精髓interceptor chain中的一环,默认情况下也是第一个拦截器,除非调用OkHttpClient.Builder#addInterceptor(Interceptor)来添加全局的拦截器。拦截关于链器的顺序参见RealCall#getResponseWithInterceptorChain()`方法。
RealCall.enqueue(Callback)过程
1 | public void enqueue(Callback responseCallback) { |
可以看到,一个 Call 只能执行一次,否则会抛异常,这里创建了一个 AsyncCall 并将Callback传入,接着再交给任务分发器 Dispatcher 来进一步处理。
1 | synchronized void enqueue(AsyncCall call) { |
从 Dispatcher#enqueue()方法的策略可以看出,对于请求的入队做了一些限制,若正在执行的请求数量小于最大值(默认64),并且此请求所属主机的正在执行任务小于最大值(默认5),就加入正在运行的队列并通过线程池来执行该任务,否则加入准备执行队列中。
- 流程图
现在回头看看 AsyncCall ,它继承自 NamedRunnable,而 NamedRunnable实现了 Runnable 接口,它的作用有2个:
①采用模板方法的设计模式,让子类将具体的操作放在 execute()方法中;
②给线程指定一个名字,比如传入模块名称,方便监控线程的活动状态;
1 | public abstract class NamedRunnable implements Runnable { |
1 | final class AsyncCall extends NamedRunnable { |
先看注释//***这里***的行finally块中执行的 client.dispatcher().finished(this)
1 | void finished(AsyncCall call) { |
其中promoteCalls()为推动下一个任务执行,其实它做的也很简单,就是在条件满足的情况下,将 readyAsyncCalls 中的任务移动到 runningAsyncCalls中,并交给线程池来执行,以下是它的实现。
1 | private void promoteCalls() { |
接下来就回到注释①处的响应内容的获取 getResponseWithInterceptorChain()
1 | Response getResponseWithInterceptorChain() throws IOException { |
可以看这块重点就是 interceptors 这个集合,首先将前面的 client.interceptors() 全部加入其中,还有在创建 RealCall时的 retryAndFollowUpInterceptor加入其中,接着还创建并添加了BridgeInterceptor、CacheInterceptor、ConnectInterceptor、CallServerInterceptor,最后通过RealInterceptorChain#proceed(Request)来执行整个 interceptor chain,可见把这个拦截器链搞清楚,整体流程也就明朗了。
RealInterceptorChain.proceed()
1 | public Response proceed(Request request) throws IOException { |
从这段实现可以看出,是按照添加到 interceptors 集合的顺序,逐个往下调用拦截器的intercept()方法,所以在前面的拦截器会先被调用。这个例子中自然就是 RetryAndFollowUpInterceptor 了。
1 | public Response intercept(Chain chain) throws IOException { |
这个拦截器就如同它的名字retry and followUp,主要负责错误处理和重定向等问题,比如路由错误、IO异常等。
接下来就到了BridgeInterceptor#intercept(),在这个拦截器中,添加了必要请求头信息,gzip处理等。
1 | public Response intercept(Chain chain) throws IOException { |
这个拦截器处理请求信息、cookie、gzip等,接着往下是 CacheInterceptor
1 | public Response intercept(Chain chain) throws IOException { |
这个拦截器主要工作是做做缓存处理,如果有有缓存并且缓存可用,那就使用缓存,否则进行调用下一个拦截器 ConnectionInterceptor 进行网络请求,并将响应内容缓存。
1 | public Response intercept(Chain chain) throws IOException { |
这个拦截器主要是打开一个到目标服务器的 connection 并调用下一个拦截器 CallServerInterceptor,这是拦截器链最后一个拦截器,它向服务器发起真正的网络请求。
1 | public Response intercept(Chain chain) throws IOException { |
从上面的请求流程图可以看出,OkHttp的拦截器链可谓是其整个框架的精髓,用户可传入的 interceptor 分为两类:
①一类是全局的 interceptor,该类 interceptor 在整个拦截器链中最早被调用,通过 OkHttpClient.Builder#addInterceptor(Interceptor) 传入;
②另外一类是非网页请求的 interceptor ,这类拦截器只会在非网页请求中被调用,并且是在组装完请求之后,真正发起网络请求前被调用,所有的 interceptor 被保存在 List

