关于Okhttp3(三)--请求流程

前两两片我们介绍了,基本使用和如何查看源码,今天我们正式进入源码分析流程。

流程图

首先我们先看一看它的请求流程,在Okhttp3中请求是基于拦截器原理,源代码如下:

源码路径:okhttp3/RealCall.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 开始执行整个请求
Response getResponseWithInterceptorChain() throws IOException {
  // Build a full stack of interceptors.
  // 拦截器栈
  List interceptors = new ArrayList<>();
  // 前文说过的 普通拦截器
  interceptors.addAll(client.interceptors());
  // 重试拦截器,网络错误、请求失败等
  interceptors.add(retryAndFollowUpInterceptor);
  // 桥接拦截器,主要是重构请求头即header
  interceptors.add(new BridgeInterceptor(client.cookieJar()));
  // 缓存拦截器
  interceptors.add(newCacheInterceptor(client.internalCache()));
  // 连接拦截器,连接服务器,https包装
  interceptors.add(new ConnectInterceptor(client));
  // 网络拦截器,websockt不支持,同样是自定义
  if (!forWebSocket) {
    interceptors.addAll(client.networkInterceptors());
  }
  // 服务拦截器,主要是发送(write、input)、读取(read、output)数据
  interceptors.add(new CallServerInterceptor(forWebSocket));

  // 开启调用链
  Interceptor.Chain chain = new RealInterceptorChain(
      interceptors, null, null, null, 0, originalRequest);
  return chain.proceed(originalRequest);
}

研究Okhttp3的源码,从此处开始,一个一个拦截器,读懂即可。不得不说这种方式真的很棒,清晰明了。

整体框架图(图片来源网络,感谢作者):

通过上图,想必对Okhttp3的实现方式,已经有了基本的认识下面我们就一步一具体分析。

构造HttpClient

HttpClient采用了建造者设计模式来实例化。本身有多个字段用于全局对象,比去Cache、Dns等

  1. 静态代码块构造全局缓存

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    
    static {
      Internal.instance = new Internal() {
        // 略
        @Override public void setCache(OkHttpClient.Builder builder, InternalCache internalCache) {
          // 写入缓存,指的是响应数据缓存
          builder.setInternalCache(internalCache);
        }
    
        // 从缓存中获取有效的连接,仅支持Http/2, 本质就是从内存的ConnectiongPol中的Deque读取
        @Override public RealConnection get(
            ConnectionPool pool, Address address, StreamAllocation streamAllocation) {
          return pool.get(address, streamAllocation);
        }
    // 将连接缓存到连接池中
        @Override public void put(ConnectionPool pool, RealConnection connection) {
          pool.put(connection);
        }
    
        // 线路的缓存,多host多ip的青款
        @Override public RouteDatabase routeDatabase(ConnectionPool connectionPool) {
          return connectionPool.routeDatabase;
        }
    
        // 略
    }
      
      // 此处有很多种数据缓存处理,不了解并不影响代码分析,如有兴趣可自行研究
    
  2. Dispatcher, 分发请求,内部是有一个ThreadPoolExecutor

  3. Proxy, 代理连接,分三种类型直接(DIRECT)、Http(http)、SOCKS。

  4. ProxySelector,线路选择器,对应Okhttp的一大特点,自行线路选择,找到合适的连接

  5. Cache, 真正的缓存实现

  6. SSLSocketFactory, Https的支持

  7. ConnectionPool, 连接池

  8. Dns,dns解析(Java实现)

  9. 其他,如超时时间等

发起请求

我们都知道一个请求有多部分组成,同样Okhttp3创建一个请求也要多部分。

  1. 构造请求头
  2. 构造请求体
  3. 发送一个请求

通过以上三步我们就可以完成一次请求。

请求头

header并非每个请求都需要,要看与服务端是如何定义的,通常一个请求会默认一些头,比如Content-Type,Accept-Encoding,Connection等对应http协议

Header本质上就是一个Map,只是在封装了一层而已,但是Okhttp3的实现不是这样,而是一个String数据,key + value的形式,即一个头占用数组的两位:

每一个色块对应一个header,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
 // 源码路径 okhttp3/Headers.java

public final class Headers {
  private final String[] namesAndValues;
  // 略
  Builder addLenient(String name, String value) {
    namesAndValues.add(name);
    namesAndValues.add(value.trim());
    return this;
  }
  // 略
 }
请求体

请求体有多种形式,对应的父类是RequestBody,有文件形式、Json等,MediaType决定了是何种形式,通常我们用的是FromBody和MultipartBody

  1. FromBody

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    
    public final class FormBody extends RequestBody {
      private static final MediaType CONTENT_TYPE =
          MediaType.parse("application/x-www-form-urlencoded");
    
      private final List encodedNames; // 参数名称
      private final List encodedValues; // 参数值
     
     // 构造者
       public static final class Builder {
        private final List names = new ArrayList<>();
        private final List values = new ArrayList<>();
    
        public Builder add(String name, String value) {
          names.add(HttpUrl.canonicalize(name, FORM_ENCODE_SET, false, false, true, true));
          values.add(HttpUrl.canonicalize(value, FORM_ENCODE_SET, false, false, true, true));
          return this;
        }
        
         //构造一个实例
        public FormBody build() {
          return new FormBody(names, values);
        }
      }
     }
    
  2. MultipartBody

    MultipartBody原理基本一致,区别在于他可以发送表单的同时也可以发送文件数据,再次不在赘述。

构造一个Request

有了上面两个步骤,接下了就自然而让产生一个Request,顾名思义它就是对请求的封装,包括请求方式,请求头,请求体,请求路径等,源代码码也是比较简单,一看即明白。

1
2
3
4
5
6
7
public final class Request {
  final HttpUrl url;
  final String method;
  final Headers headers;
  final RequestBody body;
  final Object tag;
 }
Call

有了准备工作,现在我们就要发射了,Call是一个概念的封装,就像一列火车,蓄势待发,在铁轨停靠准备出发一样

Call是一个接口,实现类只有一个RealCall,上面我们提到的流程就是在RealCall中。

1
2
3
4
5
6
7
8
9
10
11
12
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);
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
// 重点看看中文注释处即可
final class RealCall implements Call {
  
  final OkHttpClient client;
  // 重试拦截器
  final RetryAndFollowUpInterceptor retryAndFollowUpInterceptor;

  /** The application's original request unadulterated by redirects or auth headers. */
  final Request originalRequest;
  final boolean forWebSocket;

  // Guarded by this.
  private boolean executed;

  RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    this.client = client;
    this.originalRequest = originalRequest;
    this.forWebSocket = forWebSocket;
    this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
  }

  @Override public Request request() {
    return originalRequest;
  }

  // 同步执行的实现
  @Override public Response execute() throws IOException {
    // 一个call只能执行一次
    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);
    }
  }

  private void captureCallStackTrace() {
    Object callStackTrace = Platform.get().getStackTraceForCloseable("response.body().close()");
    retryAndFollowUpInterceptor.setCallStackTrace(callStackTrace);
  }

  //  异步执行,不管行返回结果
  @Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    // AsyncCall是一个Runnable的实现类,同时一个是RealCall的内部类
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }

  @Override public void cancel() {
    retryAndFollowUpInterceptor.cancel();
  }

  @Override public synchronized boolean isExecuted() {
    return executed;
  }

  @Override public boolean isCanceled() {
    return retryAndFollowUpInterceptor.isCanceled();
  }

  @SuppressWarnings("CloneDoesntCallSuperClone") // We are a final type & this saves clearing state.
  @Override public RealCall clone() {
    return new RealCall(client, originalRequest, forWebSocket);
  }

  StreamAllocation streamAllocation() {
    return retryAndFollowUpInterceptor.streamAllocation();
  }

  // 异步执行的线程封装,Android基本就是这里了
  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 {
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }
  }

  /**
   * Returns a string that describes this call. Doesn't include a full URL as that might contain
   * sensitive information.
   */
  String toLoggableString() {
    return (isCanceled() ? "canceled " : "")
        + (forWebSocket ? "web socket" : "call")
        + " to " + redactedUrl();
  }

  String redactedUrl() {
    return originalRequest.url().redact();
  }
	// 最重要的入口了
  	// 最重要的入口了
  	// 最重要的入口了
  // 重要事情说三遍
  Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List 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);
  }
}

看来上面的代码,是不是被人家的想法折服。更精妙的还在后面呢,不要急。到此,一个请求已经完成,发送和接收,(不关心内部实现的话)。接下来我们再来看看,连接器是如何工作的。

拦截器工作原理

Interceptor是一个接口,主要是对请求和响应的处理,而实现拦截器调用链的是其内部接口Chain

1
2
3
4
5
6
7
8
9
10
11
12
13
public interface Interceptor {
// 管拦截
  Response intercept(Chain chain) throws IOException;

  interface Chain {
    Request request();

    // 管分发,前行
    Response proceed(Request request) throws IOException;

    Connection connection();
  }
}

Interceptor.Chain的实现类只有一个RealInterceptorChain,也是处理调用过程的实现,其内部有个List装载了所有拦截器,大家想必也猜到了,对,就是迭代,当然也不是简简单单的接待了事。让我们看看源码实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// RealCall.getResponseWithInterceptorChain()中创建了一个实例
public final class RealInterceptorChain implements Interceptor.Chain {
  // RealCall.getResponseWithInterceptorChain()中已经赋值
   private final Request request;
  private final List interceptors;
  
  // 下面属性会在执行拦截器的过程中一步一步赋值
  private final StreamAllocation streamAllocation;
  private final HttpCodec httpCodec;
  private final Connection connection;
  
  private final int index;
  private int calls;

  public RealInterceptorChain(List interceptors, StreamAllocation streamAllocation,
      HttpCodec httpCodec, Connection connection, int index, Request request) {
    this.interceptors = interceptors;
    this.connection = connection;
    this.streamAllocation = streamAllocation;
    this.httpCodec = httpCodec;
    this.index = index;
    this.request = request;
  }

  // 实现了父类proceed方法
  @Override public Response proceed(Request request) throws IOException {
    return proceed(request, streamAllocation, httpCodec, connection);
  }

  // 处理调用
  public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      Connection connection) throws IOException {
    // 1、迭代拦截器集合
    if (index >= interceptors.size()) throw new AssertionError();
    calls++;
    // 略。。。
    // 2、创建行的实例, 并将计数器+1
    // Call the next interceptor in the chain.
    RealInterceptorChain next = new RealInterceptorChain(
        interceptors, streamAllocation, httpCodec, connection, index + 1, request);
    // 3、 取出下一个拦截器
    Interceptor interceptor = interceptors.get(index);
    // 4、执行拦截器方法,拦截器中又会调用proceed()方法
    Response response = interceptor.intercept(next);
  // 略。。。
    return response;
  }
}

看了上面的代码是不是还不明白,到底怎么实现的,实际上就是迭代+递归。

每一个RealInterceptorChain会对应一个Interceptor,然后Interceptor在产生下一个RealInterceptorChain,知道List迭代完成。

总结

Okhttp3的调用流程基本原理就是这样,重要的是思想,整个流程一气呵成,完全解耦。

你可能感兴趣的:(关于Okhttp3(三)--请求流程)