okHttp 框架源碼學習

Retrofit,OkHttp,Okio Square 安卓平臺網絡層三板斧源碼學習
基于 okhttp 3.9.0 版本 okhttp github 地址

使用方式

OkHttpClient client = new OkHttpClient();

String run(String url) throws IOException {
  Request request = new Request.Builder()
      .url(url)
      .build();

  Response response = client.newCall(request).execute();
  return response.body().string();
}
  1. 構造一個 OkHttpClient
  2. 構造一個 Request
  3. 調用 OkHttpClient.newCall(Request request) 獲得一個 Call 對象
  4. 執行 Call.execute() 獲得 Response 對象
  5. 通過 Response.body() 獲得 ResponseBody 對象

OkHttpClient 創建 http 請求源碼分析。

OkHttpClient 和 OkHttpClient.Builder

OkHttpClient 對象的創建使用了『建造者模式』

    public Builder() {
        dispatcher = new Dispatcher();
        protocols = DEFAULT_PROTOCOLS;
        connectionSpecs = DEFAULT_CONNECTION_SPECS;
        eventListenerFactory = EventListener.factory(EventListener.NONE);
        proxySelector = ProxySelector.getDefault();
        cookieJar = CookieJar.NO_COOKIES;
        socketFactory = SocketFactory.getDefault();
        hostnameVerifier = OkHostnameVerifier.INSTANCE;
        certificatePinner = CertificatePinner.DEFAULT;
        proxyAuthenticator = Authenticator.NONE;
        authenticator = Authenticator.NONE;
        connectionPool = new ConnectionPool();
        dns = Dns.SYSTEM;
        followSslRedirects = true;
        followRedirects = true;
        retryOnConnectionFailure = true;
        connectTimeout = 10_000;
        readTimeout = 10_000;
        writeTimeout = 10_000;
        pingInterval = 0;
    }

OkHttpClient.Builder 主要用來設置超時時間、代理、緩存、攔截器等。

然后調用

    public OkHttpClient build() {
        return new OkHttpClient(this);
    }

創建 OkHttpClient

 OkHttpClient(Builder builder) {
    this.dispatcher = builder.dispatcher;
    this.proxy = builder.proxy;
    this.protocols = builder.protocols;
    this.connectionSpecs = builder.connectionSpecs;
    this.interceptors = Util.immutableList(builder.interceptors);
    this.networkInterceptors = Util.immutableList(builder.networkInterceptors);
    this.eventListenerFactory = builder.eventListenerFactory;
    ……
}
Request 和 Request.Builder

Request 同樣也是使用『建造者模式』

Request(Builder builder) {
    this.url = builder.url;
    this.method = builder.method;
    this.headers = builder.headers.build();
    this.body = builder.body;
    this.tag = builder.tag != null ? builder.tag : this;
}

Request 主要為了設置 url 、請求方法(GET、POST等)、headers、請求體。

其中有個 tag 比較特殊

    /**
     * Attaches {@code tag} to the request. It can be used later to cancel the request. If the tag
     * is unspecified or null, the request is canceled by using the request itself as the tag.
     */
    public Builder tag(Object tag) {
        this.tag = tag;
        return this;
    }

根據注釋可以看出 tag 主要用來取消請求。

如果發起 POST 請求,需要使用一個 RequestBody

okhttp_01.png

RequestBody 主要用來設置不同的 POST 請求內容(字節流、文件、字符串)

分析 Call 對象
client.newCall(request)

OkHttpClient 的 newCall

@Override
public Call newCall(Request request) {
    return RealCall.newRealCall(this, request, false /* for web socket */);
}

發現請求代理給了 RealCall

static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    // Safely publish the Call instance to the EventListener.
    RealCall call = new RealCall(client, originalRequest, forWebSocket);
    call.eventListener = client.eventListenerFactory().create(call);
    return call;
}

看下 RealCall 的構造函數

private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    this.client = client;
    this.originalRequest = originalRequest;
    this.forWebSocket = forWebSocket;
    this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
}
獲得 Call.execute() 和 Call.enqueue(Callback responseCallback)

Call.execute() 負責同步請求。

Call.enqueue(Callback responseCallback) 負責異步請求。

@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);
    }
}

@Override
public void enqueue(Callback responseCallback) {
    synchronized (this) {
        if (executed) throw new IllegalStateException("Already Executed");
        executed = true;
    }
    captureCallStackTrace();
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
}

從上面代碼可以看出 Call 對象會交給 Dispatcher 對象進行管理。

executed() 方法會把 Call 對象存放在 runningSyncCalls 隊列

synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
}

enqueue() 方法會把 Call 對象存放在 runningAsyncCalls 隊列,如果隊列已滿則會被存放在 readyAsyncCalls 隊列

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

然后會執行到 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);
}

這里可以看到添加了一些列的 Interceptor 對象。這些 intercept 分別負責網絡請求、緩存、壓縮等功能。

而這些 intercept 組合的方式就是『責任鏈模式』,而最后一個 CallServerInterceptor 會真正發起網絡請求。

1. 首先會創建一個 RealInterceptorChain ,傳入所有的 Interceptor,index = 0
2. 然后執行 RealInterceptorChain.proceed(Request request)
3. 再調用 RealInterceptorChain.proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
   RealConnection connection)
4. 創建一個新的 RealInterceptorChain ,傳入 index 值 +1,對象名稱為 next
5. 獲取 interceptors 中 index 位置的 Interceptor,調用 Interceptor.intercept(next)
6. 在 interceptors 中添加的各種 Interceptor 的 intercept 中都會如下
    public Response intercept(Chain chain) throws IOException {
        ……
        chain.proceed(requestBuilder.build());
        // or
        realChain.proceed(request, streamAllocation, null, null);
        ……
    }
7. 其中 Chain.proceed() 方法又會重復執行 3、4、5、6 步驟,直到所有的 interceptors 被遍歷。
8. 最后添加的 ConnectInterceptor 和 CallServerInterceptor 是發起網絡請求的關鍵

總結以上流程如下

okhttp_02.png

發起網絡請求

先看 ConnectInterceptor

@Override
public Response intercept(Chain chain) throws IOException {
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    Request request = realChain.request();
    StreamAllocation streamAllocation = realChain.streamAllocation();

    // 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);
    RealConnection connection = streamAllocation.connection();

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

其中 StreamAllocation 對象由 RetryAndFollowUpInterceptor 創建并傳入到『責任鏈』中。

public Response intercept(Chain chain) throws IOException {
    ……
    streamAllocation = new StreamAllocation(client.connectionPool(), createAddress(request.url()),
            call, eventListener, callStackTrace);

    ……
            response = realChain.proceed(request, streamAllocation, null, null);
    ……
    }
}

可以看出 ConnectInterceptor 主要作用就是通過 StreamAllocation 創建了一個 HttpCodec。

public HttpCodec newStream(
        OkHttpClient client, Interceptor.Chain chain, boolean doExtensiveHealthChecks) {
    int connectTimeout = chain.connectTimeoutMillis();
    int readTimeout = chain.readTimeoutMillis();
    int writeTimeout = chain.writeTimeoutMillis();
    boolean connectionRetryEnabled = client.retryOnConnectionFailure();

    try {
        RealConnection existingConnection = connection;

        RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
                writeTimeout, connectionRetryEnabled, doExtensiveHealthChecks);
        HttpCodec resultCodec = resultConnection.newCodec(client, chain, this);

        if (existingConnection != connection) {
            eventListener.connectionAcquired(call, connection);
        }

        synchronized (connectionPool) {
            codec = resultCodec;
            return resultCodec;
        }
    } catch (IOException e) {
        throw new RouteException(e);
    }
}

通過上面代碼我們可以看出雖然返回的只是一個 HttpCodec 但是還會創建一個 RealConnection 。而 RealConnection 則是負責連接服務器發送請求的類。

findHealthyConnection() 方法會調用 findConnection() 方法

private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
                                      boolean connectionRetryEnabled) throws IOException {
    ……
    RealConnection result;
    ……

    // Do TCP + TLS handshakes. This is a blocking operation.
    result.connect(
            connectTimeout, readTimeout, writeTimeout, connectionRetryEnabled, call, eventListener);
    ……
}

并且調用 RealConnection 的 connect() 方法進行連接

public void connect(int connectTimeout, int readTimeout, int writeTimeout,
                    boolean connectionRetryEnabled, Call call, EventListener eventListener) {
    ……
                connectSocket(connectTimeout, readTimeout, call, eventListener);
    ……
}

private void connectSocket(int connectTimeout, int readTimeout, Call call,
                           EventListener eventListener) throws IOException {
    Proxy proxy = route.proxy();
    Address address = route.address();

    rawSocket = proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.HTTP
            ? address.socketFactory().createSocket()
            : new Socket(proxy);

    eventListener.connectStart(call, route.socketAddress(), proxy);
    rawSocket.setSoTimeout(readTimeout);
    try {
        Platform.get().connectSocket(rawSocket, route.socketAddress(), connectTimeout);
    }
    ……
    
    try {
        source = Okio.buffer(Okio.source(rawSocket));
        sink = Okio.buffer(Okio.sink(rawSocket));
    } ……
}

這里可以看出 connect() 方法會簡歷一個 Socket 連接,并把 Socket 的輸入/輸出流交包裝成 Okio 的 Source 和 Sink 對象。

然后到 CallServerInterceptor 中

public Response intercept(Chain chain) throws IOException {
    ……
    Response.Builder responseBuilder = null;
    if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
        ……
        //寫入 request body
        ……
    }

    httpCodec.finishRequest(); // 通過 Socket OutputStream 發送請求

    ……
    if (responseBuilder == null) {
        realChain.eventListener().responseHeadersStart(realChain.call());
        responseBuilder = httpCodec.readResponseHeaders(false);
    }
    Response response = responseBuilder
            .request(request)
            .handshake(streamAllocation.connection().handshake())
            .sentRequestAtMillis(sentRequestMillis)
            .receivedResponseAtMillis(System.currentTimeMillis())
            .build();

    ……
    return response;
}

總計流程圖如下

okhttp_03.png
okhttp_04.png

參考資料

OkHttp

拆輪子系列:拆 OkHttp

Okio 框架源碼學習

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容