1. 程式人生 > >okhttp原始碼解析

okhttp原始碼解析

前言

http請求的功能應該是很簡單,只是為了魯棒性和效能需要寫很多的程式碼,發現okhttp還是挺複雜的,但是我們這裡還是要好好的搞定他。

正文

我們從最簡單的使用開始

OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
        .url(url)
        .build();
Response response = client.newCall(request).execute()) {
      return response.body().string();

OkHttpClient,就是一個客戶操作的控制代碼,肯定是初始化了一大堆的預設引數,看先原始碼

  public OkHttpClient() {
    this(new Builder());
  }
OkHttpClient(Builder builder) {
}

看到builder,我們就放心了(而不是一個庫會有兩種風格)。直接new出來,大概就是全部用預設引數,request大概也是那樣,我們還是直接看client的操作。 下面我們看真的請求過程。

client.newCall(request).execute()

我們看下 到底是如何創造出一個call的。主要程式碼如下:

    RealCall call = new RealCall(client, originalRequest, forWebSocket);
    //這個暫時沒啥用,是個空的類
    call.eventListener = client.eventListenerFactory().create(call);

其實就是一個client有一個RealCall的類似建構函式的東西,.eventListener是現在是個空,啥也不幹。現在回到一個最終極的複雜的問題,RealCallexecute()

 @Override
public Response execute() throws
IOException { ...... //這段diamante比較奇怪,暫時沒搞懂到底要幹嘛,貌似是是為了呼叫週期。 client.dispatcher().executed(this); Response result = getResponseWithInterceptorChain(); return result; ...... } 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)); RealInterceptorChain chain = new RealInterceptorChain(interceptors, null, null, null, 0, originalRequest, this, eventListener, client.connectTimeoutMillis(), client.readTimeoutMillis(), client.writeTimeoutMillis()); return chain.proceed(originalRequest); }

看到後半部分才是核心啊,這裡搞了一個職責鏈模式,最後通過這個東東來完成所有請求的內容,這裡我們還是要好好的研讀一番。RealInterceptorChain這個東西其實是一個用來遞迴呼叫的類,我們具體來看他的proceed函式.

public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      RealConnection connection) throws IOException {
    ......
    calls++;
    ......
    // Call the next interceptor in the chain.
    RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
        connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
        writeTimeout);
    Interceptor interceptor = interceptors.get(index);
    //這裡類似一個遞迴呼叫,第一個interceptor種有個迴圈,用來遞迴呼叫此函式,返回的時候,是真的response部為空的時候,
    Response response = interceptor.intercept(next);

    return response;
  }

其實我們很容易發現這個東西,就是在new一個自己(當然因為職責鏈已經開始工作,所以只用後一個index即可),然後傳入給第一個職責者即可,我們看下第一個職責者做了什麼,是不是又回到這個函式,驗證我們這是一個迴圈的猜想,第一個是RetryAndFollowUpInterceptor

 @Override
    public Response intercept(Chain chain) throws IOException {
        ......
        //這是職責鏈模式,第一個只是為了處理錯誤等,所以出現一個迴圈,其他的任何一個攔截器,出現問題,都應該在這裡重新處理。
        while (true) {
            ......
                response = realChain.proceed(request, streamAllocation, null, null);
                }
       ......
    }

這個函式灰常複雜,這裡暫時不在詳細研究,總之就是又一次遞迴呼叫我我們剛才RealInterceptorChain的process方法。那麼根據之前,他會呼叫了第二個職責者。因為這個主要是用來處理異常情況,我們不在詳細分析程式碼。我們直接分析後面真正的網路強求部分的職責。第二個職責者是BridgeInterceptor

/**
 * Bridges from application code to network code. First it builds a network request from a user
 * request. Then it proceeds to call the network. Finally it builds a user response from the network
 * response.
 */

@Override
    public Response intercept(Chain chain) throws IOException {
        Request userRequest = chain.request();
        Request.Builder requestBuilder = userRequest.newBuilder();

        RequestBody body = userRequest.body();
        ......
        初始化請求的host
        if (userRequest.header("Host") == null) {
            requestBuilder.header("Host", hostHeader(userRequest.url(), false));
        }
        //這些都是請求的一些引數,可以暫時無視。
        if (userRequest.header("Connection") == null) {
            requestBuilder.header("Connection", "Keep-Alive");
        }

        // If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing
        // the transfer stream.
        boolean transparentGzip = false;
        if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
            transparentGzip = true;
            requestBuilder.header("Accept-Encoding", "gzip");
        }
        //Cookie這裡已經load,有些內容可以不用請求
        List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
        if (!cookies.isEmpty()) {
            requestBuilder.header("Cookie", cookieHeader(cookies));
        }

        if (userRequest.header("User-Agent") == null) {
            requestBuilder.header("User-Agent", Version.userAgent());
        }

        //chain 已經改變了,這裡request 也已經改變了。這裡又到了職責鏈的下一步,用來請求後來者來處理,
        Response networkResponse = chain.proceed(requestBuilder.build());

       
    }

註釋比較明確,者是一個橋接使用者和網路請求的中間者,主要是生成一些網路請求的內容,我們可以看程式碼,他確實是初始化一些請求的資訊,比如host,Accept-Encoding等變數。這裡我們不在詳細介紹。我們直接看下一個職責者。這是CacheInterceptor

/** Serves requests from the cache and writes responses to the cache. */


 @Override public Response intercept(Chain chain) throws IOException {
 ......
 //這裡其實就是剛才初始化完成的東東。不需要介意。
 Request networkRequest = strategy.networkRequest;
    ......
      networkResponse = chain.proceed(networkRequest);
   ......
  }

註釋那麼明確,從註釋中看到這就是從cache中載入請求內容,和吧載入的內容寫入cache.cache這些東東不太是我們關注的焦點,我們暫時先放下吧,直接進入下一個請求部分。

/** Opens a connection to the target server and proceeds to the next interceptor. */


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

這個類的註釋也比較明確。開啟一個連線,然後交給後面的職責這幹活。emm這程式碼很少,確實非常複雜複雜,不想讀,不過大概就是建立一個連線,我們可以看到在StreamLoaction中可以看到

result = new RealConnection(connectionPool, selectedRoute);
......
result.connect(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis,
                connectionRetryEnabled, call, eventListener);

這是一個建立連線的部分。這裡我只是寫了部分程式碼,有些不想過分追究,有興趣的童鞋的可以斷點一下,這主要是為了建立一個ssl安全驗證的socket,這裡實在懶得過分追問,反正就是獲取一個socket的stream我們繼續看我們的真的請求的部分。 最後是一個CallServerInterceptor

/** This is the last interceptor in the chain. It makes a network call to the server. */


    public Response intercept(Chain chain) throws IOException {
        RealInterceptorChain realChain = (RealInterceptorChain) chain;
        HttpCodec httpCodec = realChain.httpStream();
        StreamAllocation streamAllocation = realChain.streamAllocation();
        RealConnection connection = (RealConnection) realChain.connection();
        Request request = realChain.request();

        long sentRequestMillis = System.currentTimeMillis();

        realChain.eventListener().requestHeadersStart(realChain.call());
        httpCodec.writeRequestHeaders(request);
        realChain.eventListener().requestHeadersEnd(realChain.call(), request);

        Response.Builder responseBuilder = null;
        if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
            // If there's a "Expect: 100-continue" header on the request, wait for a "HTTP/1.1 100
            // Continue" response before transmitting the request body. If we don't get that, return
            // what we did get (such as a 4xx response) without ever transmitting the request body.
            if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
                httpCodec.flushRequest();
                realChain.eventListener().responseHeadersStart(realChain.call());
                responseBuilder = httpCodec.readResponseHeaders(true);
            }

            if (responseBuilder == null) {
                // Write the request body if the "Expect: 100-continue" expectation was met.
                realChain.eventListener().requestBodyStart(realChain.call());
                long contentLength = request.body().contentLength();
                CountingSink requestBodyOut =
                        new CountingSink(httpCodec.createRequestBody(request, contentLength));
                BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);

                request.body().writeTo(bufferedRequestBody);
                bufferedRequestBody.close();
                realChain.eventListener()
                        .requestBodyEnd(realChain.call(), requestBodyOut.successfulCount);
            } else if (!connection.isMultiplexed()) {
                // If the "Expect: 100-continue" expectation wasn't met, prevent the HTTP/1 connection
                // from being reused. Otherwise we're still obligated to transmit the request body to
                // leave the connection in a consistent state.
                streamAllocation.noNewStreams();
            }
        }

        httpCodec.finishRequest();

        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();

        int code = response.code();
        if (code == 100) {
            // server sent a 100-continue even though we did not request one.
            // try again to read the actual response
            responseBuilder = httpCodec.readResponseHeaders(false);

            response = responseBuilder
                    .request(request)
                    .handshake(streamAllocation.connection().handshake())
                    .sentRequestAtMillis(sentRequestMillis)
                    .receivedResponseAtMillis(System.currentTimeMillis())
                    .build();

            code = response.code();
        }

        realChain.eventListener()
                .responseHeadersEnd(realChain.call(), response);

        if (forWebSocket && code == 101) {
            // Connection is upgrading, but we need to ensure interceptors see a non-null response body.
            response = response.newBuilder()
                    .body(Util.EMPTY_RESPONSE)
                    .build();
        } else {
            response = response.newBuilder()
                    .body(httpCodec.openResponseBody(response))
                    .build();
        }

        if ("close".equalsIgnoreCase(response.request().header("Connection"))
                || "close".equalsIgnoreCase(response.header("Connection"))) {
            streamAllocation.noNewStreams();
        }

        if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
            throw new ProtocolException(
                    "HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
        }

        return response;
    }

註釋告訴我們這就是真的網路請求了,媽的,到底如何請求的我就看不懂了,如果想完全搞懂,還是需要從倒數第二部,一點一點的斷點嘍。具體的http的請求過程。有機會再慢慢的驗證,這裡我就不管嘍。以後慢慢分析到底如何建立socket的。

後記

這個東西寫了挺久的,中間耽誤了一週,這次湊數把它完成,希望以後可以補充完整。