运行机制

  • OkHttp 使用各种拦截器来完成发送请求,重试和缓存等等功能。一般的网络请求,OkHttpClient 会构造一个 RealCall 方法来完成。RealCall 的 execute 方法中构建调用链并执行网络请求。
  • 构造拦截器调用链时,将所有拦截器放在 ArrayList 中,将此 List 和要执行拦截器的下标作为构造参数传入 Chain 中,调用 Chain 的 proceed 方法。
  • proceed 方法根据下标取出拦截器,新建一个 Chain,将下标自增后和 List 一起传入 Chain 中,然后以新建的 Chain 作为参数调用拦截器的 intercept 方法
  • 拦截器没有能力处理网络请求时,便会在 intercept 方法中调用 Chain 的 proceed 方法,试图从下一个拦截器那里获得 Response 并返回,这样一层一层的执行下去;
  • 最后执行到 CallServerInterceptor 的 intercept 中,这个拦截器自己有能力处理网络请求,所以没有调用 Chain 的 proceed 方法,而是进行了网络 IO,获取到 Response ,并返回。这样一级一级的返回上去,最后在 getResponseWithInterceptorChain 方法中返回。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    // 同步请求
    @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); // 同步请求时,这里只是通知 dispatcher 一下
    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); // 同上,只是通知一下
    }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//省略号的地方删除了异常判断
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec, RealConnection connection) throws IOException {

calls++;

...

// 调用拦截器链中的下一个拦截器
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;
}

各拦截器作用

由执行顺序依次排列:

  1. 用户自定义的拦截器,由 OkHttpClient 传入,在最开始执行
  2. retryAndFollowUpInterceptor。进行重试和重定向的拦截器
  3. BridgeInterceptor。将用户构造的 Request 处理成服务器需要的 Request,并将服务器返回的 Response 转换成用户理解的 Response
  4. CacheInterceptor。负责缓存的拦截器。
  5. ConnectInterceptor。和服务器进行连接的拦截器。
  6. networkInterceptors。使用 WebSocket 协议时传入的拦截器。
  7. CallServerInterceptor。进行网络 IO 的拦截器。

如何建立连接:ConnectInterceptor

1
2
3
4
5
6
7
8
9
10
11
12
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Request request = realChain.request();
StreamAllocation streamAllocation = realChain.streamAllocation(); //这个StreamAllocation在retryAndFollowUpInterceptor中创建,然后一级级传下来

// We need the network to satisfy this request. Possibly for validating a conditional GET.
boolean doExtensiveHealthChecks = !request.method().equals("GET");
HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks); // 这个方法会在连接池中找可用连接,如果没有就使用 Socket 新建一个连接,最后构建一个 HttpCodec 返回。HttpCodec 是对 Http 协议的抽象,发送和接受数据都由它完成
RealConnection connection = streamAllocation.connection(); //这里返回的就是 newStream 中找到的连接

return realChain.proceed(request, streamAllocation, httpCodec, connection);
}

发送和接收数据:CallServerInterceptor

这一步都由 HttpCodec 来完成,其内部使用 Okio,Okio 又使用 Socket 完成。

  1. 向服务器发送请求头
  2. 如果有请求体,就发送请求体
  3. 读取响应头,构造一个 Response 对象
  4. 如果有响应体,就在3的基础上再构造一个新的 Response 对象

重试和重定向:RetryAndFollowUpInterceptor

  1. 在死循环中执行请求,来实现重试机制,捕获 RouteException 和 IOException 异常时会一直重试
  2. 以下几种情况会阻止重试,抛出异常:
    • OkHttpClient 中配置了 retryOnConnectionFailure 参数为 false
    • Request 已经开始发送并且 Request 实现了 UnrepeatableRequestBody 接口
    • 异常为几种特定的异常时,包括协议内错误,IO 中断,证书错误等几种情形
    • 没有 Route 可用于尝试
  3. 请求成功后如果不需要重定向,直接返回 Response,结束循环
  4. 如果需要重定向,则更新 Request,执行下一次请求
  5. 超出最大重定向次数时会抛出异常

缓存机制:CacheInterceptor

OkHttp 实现了 Http 协议中的缓存机制,即对比缓存:

  • 判断是否设置了缓存操作类 InternalCache,没有会进行正常的网络请求
  • 如果设置了,根据 request 取出 response,如果为空,进行正常的网络请求
  • 如果不为空,利用 CacheStrategy 构造新的请求,询问缓存是否可用
  • 如果服务端根据 Request 中的 Cache-Control 判断缓存是否可用并返回
  • 如果可用,直接利用缓存构造 Response 返回
  • 如果不可用,服务端会返回新的 Resopnse,返回此 Response。

异步请求

  • 将请求包装成 AsyncCall,AsyncCall 实现了 Runnable 接口,能被线程池执行。执行时依然调用 getResponseWithInterceptorChain 方法。和同步执行一样。
  • 调用 dispatcher 的 enqueue 方法,当正在执行的请求未达到最大请求数,并且单个 Host 的请求也未达到最大数时,加入正在执行的队列,并使用线程池执行
  • 最大并发请求数默认为 64,单个 Host 最大请求数默认为 5
  • 否则加入等待队列
  • 一个异步请求执行完后,会从等待队列中取出请求继续执行
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    @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));
    }

    synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
    runningAsyncCalls.add(call);
    executorService().execute(call);
    } else {
    readyAsyncCalls.add(call);
    }
    }