OKHttp源碼解析

rbr008 8年前發布 | 53K 次閱讀 OkHttp Android開發 移動開發 HTTP Cache

前言:對于 OkHttp 我接觸的時間其實不太長,一直都是使用Retrofit + OkHttp 來做網絡請求的,但是有同學說面試的時候可能會問框架源碼,這樣光是會用是不夠的,于是便萌生了通一通OkHttp源碼的念頭。經過大約一周的時間,源碼看了個大概(說來慚愧,也就知道里面的原理),這里變向大家介紹一下我的所得,希望對大家能有所幫助。這里推薦兩篇博文: OkHttp 官方教程解析 - 徹底入門 OkHttp 使用拆輪子系列:拆 OkHttp 前者能夠讓你入門OkHttp,后者能讓你明白OkHttp的原理,我就是看的后者去看的源碼,如果看我的不太懂,大家可以去看看上面的。同時,歡迎大家交流,提出意見,謝謝!

總體流程

下面的流程圖是由上面的文章抄來的(自己畫的圖,用的visio)

整個流程是,通過 OkHttpClient 將構建的 Request 轉換為Call,然后在RealCall中進行異步或同步任務,最后通過一些的攔截器 interceptor 發出網絡請求和得到返回的 response 。

將流程大概是這么個流程,大家可以有個大概的印象,繼續向下看:

OkHttp流程圖.jpg

為了讓大家有更深的印象,我準備追蹤一個 GET 網絡請求的具體流程,來介紹在源碼中發生了什么。

GET請求過程

這是利用 OkHttp 寫一個Get請求步驟,這里是一個同步的請求,異步的下面也會說:

//HTTP GET
    public String get(String url) throws IOException {
        //新建OKHttpClient客戶端
        OkHttpClient client = new OkHttpClient();
        //新建一個Request對象
        Request request = new Request.Builder()
                .url(url)
                .build();
        //Response為OKHttp中的響應
        Response response = client.newCall(request).execute();
        if (response.isSuccessful()) {
            return response.body().string();
        }else{
            throw new IOException("Unexpected code " + response);
        }
    }

OKHttpClient:流程的總控制者

OkHttpClient的類設計圖

使用OkHttp的時候我們都會創建一個OkHttpClient對象:

OkHttpClient client = new OkHttpClient();

這是做什么的呢?看下builder里面的參數:

final Dispatcher dispatcher;  //分發器
    final Proxy proxy;  //代理
    final List<Protocol> protocols; //協議
    final List<ConnectionSpec> connectionSpecs; //傳輸層版本和連接協議
    final List<Interceptor> interceptors; //攔截器
    final List<Interceptor> networkInterceptors; //網絡攔截器
    final ProxySelector proxySelector; //代理選擇
    final CookieJar cookieJar; //cookie
    final Cache cache; //緩存
    final InternalCache internalCache;  //內部緩存
    final SocketFactory socketFactory;  //socket 工廠
    final SSLSocketFactory sslSocketFactory; //安全套接層socket 工廠,用于HTTPS
    final CertificateChainCleaner certificateChainCleaner; // 驗證確認響應證書 適用 HTTPS 請求連接的主機名。
    final HostnameVerifier hostnameVerifier;    //  主機名字確認
    final CertificatePinner certificatePinner;  //  證書鏈
    final Authenticator proxyAuthenticator;     //代理身份驗證
    final Authenticator authenticator;      // 本地身份驗證
    final ConnectionPool connectionPool;    //連接池,復用連接
    final Dns dns;  //域名
    final boolean followSslRedirects;  //安全套接層重定向
    final boolean followRedirects;  //本地重定向
    final boolean retryOnConnectionFailure; //重試連接失敗
    final int connectTimeout;    //連接超時
    final int readTimeout; //read 超時
    final int writeTimeout; //write 超時

在這些聲明的對象中可以看出來,幾乎所有用到的類都和 OkHttpClient 有關系。事實上,你能夠通過它來設置改變一些參數,因為他是通過 建造者模式 實現的,因此你可以通過 builder() 來設置。如果不進行設置,在 Builder 中就會使用默認的設置:

dispatcher = new Dispatcher();
            protocols = DEFAULT_PROTOCOLS;
            connectionSpecs = DEFAULT_CONNECTION_SPECS;
            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;

看到這,如果你還不明白的話,也沒關系,在 OkHttp 中只是設置用的的各個東西。真正的流程要從里面的 newCall() 方法中說起:

/**
        *  Prepares the {@code request} to be executed at some point in the future.
        *  準備將要被執行的request
        */
        @Override
        public Call newCall(Request request) {
            return new RealCall(this, request);
        }

當通過 建造者模式 創建了 Request 之后(這個沒什么好說),緊接著就通過下面的代碼來獲得 Response

大家還記得上面做 GET 請求時的這句代碼吧:

Response response = client.newCall(request).execute(); 這就代碼就開啟了整個GET請求的流程:

RealCall:真正的請求執行者。

先看一下他的構造方法:

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

可以看到他傳過來一個 OkHttpClient 對象和一個 originalRequest (我們創建的 Request )。

接下來看它的 execute() 方法:

@Override
    public Response execute() throws IOException {
        synchronized (this) {
            if (executed) throw new IllegalStateException("Already Executed"); //(1)
            executed = true;
        }
        try {
            client.dispatcher.executed(this);//(2)
            Response result = getResponseWithInterceptorChain();//(3)
            if (result == null) throw new IOException("Canceled");
            return result;
        }finally {
            client.dispatcher.finished(this);//(4)
        }
    }
  1. 檢查這個 call 是否已經被執行了,每個 call 只能被執行一次,如果想要一個完全一樣的 call ,可以利用 all#clone 方法進行克隆。
  2. 利用 client.dispatcher().executed(this) 來進行實際執行, dispatcher 是剛才看到的 OkHttpClient.Builder 的成員之一,它的文檔說自己是異步 HTTP 請求的執行策略,現在看來,同步請求它也有摻和。
  3. 調用 getResponseWithInterceptorChain() 函數獲取 HTTP 返回結果,從函數名可以看出,這一步還會進行一系列“攔截”操作。
  4. 最后還要通知 dispatcher 自己已經執行完畢。

    dispatcher 這里我們不過度關注,在同步執行的流程中,涉及到 dispatcher 的內容只不過是告知它我們的執行狀態,比如開始執行了(調用 executed ),比如執行完畢了(調用 finished ),在異步執行流程中它會有更多的參與。

    真正發出網絡請求,解析返回結果的,還是 getResponseWithInterceptorChain :

    //攔截器的責任鏈。
     private Response getResponseWithInterceptorChain() throws IOException {
         // Build a full stack of interceptors.
         List<Interceptor> interceptors = new ArrayList<>();
         interceptors.addAll(client.interceptors());     //(1)
         interceptors.add(retryAndFollowUpInterceptor);    //(2)
         interceptors.add(new BridgeInterceptor(client.cookieJar()));    //(3)
         interceptors.add(new CacheInterceptor(client.internalCache()));    //(4)
         interceptors.add(new ConnectInterceptor(client));    //(5)
         if (!retryAndFollowUpInterceptor.isForWebSocket()) {
             interceptors.addAll(client.networkInterceptors());    //(6)
         }
         interceptors.add(new CallServerInterceptor(
                 retryAndFollowUpInterceptor.isForWebSocket()));     //(7)
    
         Interceptor.Chain chain = new RealInterceptorChain(
                 interceptors, null, null, null, 0, originalRequest);
         return chain.proceed(originalRequest); //  <<=========開始鏈式調用
     }
  1. 在配置 OkHttpClient 時設置的 interceptors ;
  2. 負責失敗重試以及重定向的 RetryAndFollowUpInterceptor ;
  3. 負責把用戶構造的請求轉換為發送到服務器的請求、把服務器返回的響應轉換為用戶友好的響應的 BridgeInterceptor ;
  4. 負責讀取緩存直接返回、更新緩存的 CacheInterceptor ;
  5. 負責和服務器建立連接的 ConnectInterceptor ;
  6. 配置 OkHttpClient 時設置的 networkInterceptors ;
  7. 負責向服務器發送請求數據、從服務器讀取響應數據的 CallServerInterceptor 。
  8. 在 return chain.proceed(originalRequest); 中開啟鏈式調用:

RealInterceptorChain

public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      Connection connection) throws IOException {
    if (index >= interceptors.size()) throw new AssertionError();

    calls++;

    // If we already have a stream, confirm that the incoming request will use it.
    //如果我們已經有一個stream。確定即將到來的request會使用它
    if (this.httpCodec != null && !sameConnection(request.url())) {
      throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
          + " must retain the same host and port");
    }

    // If we already have a stream, confirm that this is the only call to chain.proceed().
    //如果我們已經有一個stream, 確定chain.proceed()唯一的call
    if (this.httpCodec != null && calls > 1) {
      throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
          + " must call proceed() exactly once");
    }

    // Call the next interceptor in the chain.
    //調用鏈的下一個攔截器
    RealInterceptorChain next = new RealInterceptorChain(
        interceptors, streamAllocation, httpCodec, connection, index + 1, request);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);

    // Confirm that the next interceptor made its required call to chain.proceed().
    if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
      throw new IllegalStateException("network interceptor " + interceptor
          + " must call proceed() exactly once");
    }

    // Confirm that the intercepted response isn't null.
    if (response == null) {
      throw new NullPointerException("interceptor " + interceptor + " returned null");
    }

    return response;
  }
`

代碼很多,但是主要是進行一些判斷,主要的代碼在這:

// Call the next interceptor in the chain.
    //調用鏈的下一個攔截器
    RealInterceptorChain next = new RealInterceptorChain(
        interceptors, streamAllocation, httpCodec, connection, index + 1, request);    //(1)
    Interceptor interceptor = interceptors.get(index);     //(2)
    Response response = interceptor.intercept(next);    //(3)
  1. 實例化下一個攔截器對應的 RealIterceptorChain 對象,這個對象會在傳遞給當前的攔截器
  2. 得到當前的攔截器: interceptors 是存放攔截器的 ArryList
  3. 調用當前攔截器的 intercept() 方法,并將下一個攔截器的 RealIterceptorChain 對象傳遞下去
    除了在client中自己設置的 interceptor ,第一個調用的就是 retryAndFollowUpInterceptor

    RetryAndFollowUpInterceptor:負責失敗重試以及重定向

    直接上代碼
@Override 
public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        streamAllocation = new StreamAllocation(
                client.connectionPool(), createAddress(request.url()));
        int followUpCount = 0;
        Response priorResponse = null;
        while (true) {
            if (canceled) {
                streamAllocation.release();
                throw new IOException("Canceled");
            }

            Response response = null;
            boolean releaseConnection = true;
            try {
                response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);    //(1)
                releaseConnection = false;
            } catch (RouteException e) {
                // The attempt to connect via a route failed. The request will not have been sent.
                //通過路線連接失敗,請求將不會再發送
                if (!recover(e.getLastConnectException(), true, request)) throw e.getLastConnectException();
                releaseConnection = false;
                continue;
            } catch (IOException e) {
                // An attempt to communicate with a server failed. The request may have been sent.
                // 與服務器嘗試通信失敗,請求不會再發送。
                if (!recover(e, false, request)) throw e;
                releaseConnection = false;
                continue;
            } finally {
                // We're throwing an unchecked exception. Release any resources.
                //拋出未檢查的異常,釋放資源
                if (releaseConnection) {
                    streamAllocation.streamFailed(null);
                    streamAllocation.release();
                }
            }

            // Attach the prior response if it exists. Such responses never have a body.
            // 附加上先前存在的response。這樣的response從來沒有body
            // TODO: 2016/8/23 這里沒賦值,豈不是一直為空?
            if (priorResponse != null) { //  (2)
                response = response.newBuilder()
                        .priorResponse(priorResponse.newBuilder()
                                .body(null)
                                .build())
                        .build();
            }

            Request followUp = followUpRequest(response); //判斷狀態碼 (3)
            if (followUp == null){
                if (!forWebSocket) {
                    streamAllocation.release();
                }
                return response;
            }

            closeQuietly(response.body());

            if (++followUpCount > MAX_FOLLOW_UPS) {
                streamAllocation.release();
                throw new ProtocolException("Too many follow-up requests: " + followUpCount);
            }

            if (followUp.body() instanceof UnrepeatableRequestBody) {
                throw new HttpRetryException("Cannot retry streamed HTTP body", response.code());
            }

            if (!sameConnection(response, followUp.url())) {
                streamAllocation.release();
                streamAllocation = new StreamAllocation(
                        client.connectionPool(), createAddress(followUp.url()));
            } else if (streamAllocation.codec() != null) {
                throw new IllegalStateException("Closing the body of " + response
                        + " didn't close its backing stream. Bad interceptor?");
            }

            request = followUp;
            priorResponse = response;
        }
    }
  1. 這里是最關鍵的代碼,可以看出在 response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null); 中直接調用了下一個攔截器,然后捕獲可能的異常來進行操作
  2. 這里沒看太懂,有點坑,以后補
  3. 這里對于返回的response的狀態碼進行判斷,然后進行處理

BridgeInterceptor:

負責把用戶構造的請求轉換為發送到服務器的請求、把服務器返回的響應轉換為用戶友好的響應的 。

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

    //檢查request。將用戶的request轉換為發送到server的請求
    RequestBody body = userRequest.body();     //(1)
    if (body != null) {
      MediaType contentType = body.contentType();
      if (contentType != null) {
        requestBuilder.header("Content-Type", contentType.toString());
      }

      long contentLength = body.contentLength();
      if (contentLength != -1) {
        requestBuilder.header("Content-Length", Long.toString(contentLength));
        requestBuilder.removeHeader("Transfer-Encoding");
      } else {
        requestBuilder.header("Transfer-Encoding", "chunked");
        requestBuilder.removeHeader("Content-Length");
      }
    }

    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.
    //GZIP壓縮
    boolean transparentGzip = false;
    if (userRequest.header("Accept-Encoding") == null) {
      transparentGzip = true;
      requestBuilder.header("Accept-Encoding", "gzip");
    }

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

    Response networkResponse = chain.proceed(requestBuilder.build());   //(2)

    HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers()); //(3)

    Response.Builder responseBuilder = networkResponse.newBuilder()
        .request(userRequest);

    if (transparentGzip
        && "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
        && HttpHeaders.hasBody(networkResponse)) {
      GzipSource responseBody = new GzipSource(networkResponse.body().source());
      Headers strippedHeaders = networkResponse.headers().newBuilder()
          .removeAll("Content-Encoding")
          .removeAll("Content-Length")
          .build();
      responseBuilder.headers(strippedHeaders);
      responseBuilder.body(new RealResponseBody(strippedHeaders, Okio.buffer(responseBody)));
    }

    return responseBuilder.build();
  }
  1. 在(1)和(2)之間, BridgeInterceptor 對于 request 的格式進行檢查,讓構建了一個新的 request
  2. 調用下一個 interceptor 來得到response
  3. (3)下面就是對得到的response進行一些判斷操作,最后將結果返回。
@Override 
public Response intercept(Chain chain) throws IOException {
    Response cacheCandidate = cache != null        //=============(1)
        ? cache.get(chain.request()) //通過request得到緩存
        : null;

    long now = System.currentTimeMillis();

    CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get(); //根據request來得到緩存策略===========(2)
    Request networkRequest = strategy.networkRequest;
    Response cacheResponse = strategy.cacheResponse;

    if (cache != null) {
      cache.trackResponse(strategy);
    }

    if (cacheCandidate != null && cacheResponse == null) { //存在緩存的response,但是不允許緩存
      closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it. 緩存不適合,關閉
    }

    // If we're forbidden from using the network and the cache is insufficient, fail.
      //如果我們禁止使用網絡,且緩存為null,失敗
    if (networkRequest == null && cacheResponse == null) {
      return new Response.Builder()
          .request(chain.request())
          .protocol(Protocol.HTTP_1_1)
          .code(504)
          .message("Unsatisfiable Request (only-if-cached)")
          .body(EMPTY_BODY)
          .sentRequestAtMillis(-1L)
          .receivedResponseAtMillis(System.currentTimeMillis())
          .build();
    }

    // If we don't need the network, we're done.
    if (networkRequest == null) {  //沒有網絡請求,跳過網絡,返回緩存
      return cacheResponse.newBuilder()
          .cacheResponse(stripBody(cacheResponse))
          .build();
    }

    Response networkResponse = null;
    try {
      networkResponse = chain.proceed(networkRequest);//網絡請求攔截器    //======(3)
    } finally {
      // If we're crashing on I/O or otherwise, don't leak the cache body.
        //如果我們因為I/O或其他原因崩潰,不要泄漏緩存體
      if (networkResponse == null && cacheCandidate != null) {
        closeQuietly(cacheCandidate.body());
      }
    }

    // If we have a cache response too, then we're doing a conditional get.========(4)
      //如果我們有一個緩存的response,然后我們正在做一個條件GET
    if (cacheResponse != null) {
      if (validate(cacheResponse, networkResponse)) { //比較確定緩存response可用
        Response response = cacheResponse.newBuilder()
            .headers(combine(cacheResponse.headers(), networkResponse.headers()))
            .cacheResponse(stripBody(cacheResponse))
            .networkResponse(stripBody(networkResponse))
            .build();
        networkResponse.body().close();

        // Update the cache after combining headers but before stripping the
        // Content-Encoding header (as performed by initContentStream()).
          //更新緩存,在剝離content-Encoding之前
        cache.trackConditionalCacheHit();
        cache.update(cacheResponse, response);
        return response;
      } else {
        closeQuietly(cacheResponse.body());
      }
    }

    Response response = networkResponse.newBuilder()
        .cacheResponse(stripBody(cacheResponse))
        .networkResponse(stripBody(networkResponse))
        .build();

    if (HttpHeaders.hasBody(response)) {    // =========(5)
      CacheRequest cacheRequest = maybeCache(response, networkResponse.request(), cache);
      response = cacheWritingResponse(cacheRequest, response);
    }

    return response;
  }
  1. 首先,根據 request 來判斷 cache 中是否有緩存的 response ,如果有,得到這個 response ,然后進行判斷當前 response 是否有效,沒有將 cacheCandate 賦值為空。
  2. 根據request判斷緩存的策略,是否要使用了網絡,緩存 或兩者都使用
  3. 調用下一個攔截器,決定從網絡上來得到 response
  4. 如果本地已經存在 cacheResponse ,那么讓它和網絡得到的 networkResponse 做比較,決定是否來更新緩存的 cacheResponse
  5. 緩存未經緩存過的 response

    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, doExtensiveHealthChecks);
        RealConnection connection = streamAllocation.connection();
    
        return realChain.proceed(request, streamAllocation, httpCodec, connection);
    }

實際上建立連接就是創建了一個 HttpCodec 對象,它將在后面的步驟中被使用,那它又是何方神圣呢?它是對 HTTP 協議操作的抽象,有兩個實現: Http1Codec 和 Http2Codec ,顧名思義,它們分別對應 HTTP/1.1 和 HTTP/2 版本的實現。

在Http1Codec中,它利用 Okio 對Socket的讀寫操作進行封裝, Okio 以后有機會再進行分析,現在讓我們對它們保持一個簡單地認識:它對java.io和java.nio進行了封裝,讓我們更便捷高效的進行 IO 操作。

而創建 HttpCodec 對象的過程涉及到 StreamAllocation、RealConnection ,代碼較長,這里就不展開,這個過程概括來說,就是找到一個可用的 RealConnection ,再利用 RealConnection 的輸入輸出( BufferedSource 和 BufferedSink )創建 HttpCodec 對象,供后續步驟使用。

NetworkInterceptors

配置OkHttpClient時設置的 NetworkInterceptors。

CallServerInterceptor:發送和接收數據

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

    long sentRequestMillis = System.currentTimeMillis();
    httpCodec.writeRequestHeaders(request);

    if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {   //===(1)
      Sink requestBodyOut = httpCodec.createRequestBody(request, request.body().contentLength());
      BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
      request.body().writeTo(bufferedRequestBody);
      bufferedRequestBody.close();
    }

    httpCodec.finishRequest();

    Response response = httpCodec.readResponseHeaders()     //====(2)
        .request(request)
        .handshake(streamAllocation.connection().handshake())
        .sentRequestAtMillis(sentRequestMillis)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build();

    if (!forWebSocket || response.code() != 101) {
      response = response.newBuilder()
          .body(httpCodec.openResponseBody(response))
          .build();
    }

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

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

    return response;
  }
  1. 檢查請求方法,用 Httpcodec 處理 request
  2. 進行網絡請求得到 response
  3. 返回r esponse

總結

前面說了攔截器用了 責任鏈設計模式 ,它將請求一層一層向下傳,知道有一層能夠得到Resposne就停止向下傳遞,然后將 response 向上面的攔截器傳遞,然后各個攔截器會對 respone 進行一些處理,最后會傳到 RealCall 類中通過 execute 來得到 esponse 。

異步請求的流程:

異步get請求示例如下:

private final OkHttpClient client = new OkHttpClient();

  public void run() throws Exception {
    Request request = new Request.Builder()
        .url("http://publicobject.com/helloworld.txt")
        .build();

    client.newCall(request).enqueue(new Callback() {
      @Override 
      public void onFailure(Call call, IOException e) {
        e.printStackTrace();
      }

      @Override 
      public void onResponse(Call call, Response response) throws IOException {
        if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

        Headers responseHeaders = response.headers();
        for (int i = 0, size = responseHeaders.size(); i < size; i++) {
          System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
        }

        System.out.println(response.body().string());
      }
    });
  }

由代碼中 client.newCall(request).enqueue(Callback) ,開始我們知道 client.newCall(request) 方法返回的是 RealCall 對象,接下來繼續向下看 enqueue() 方法:

//異步任務使用
    @Override 
    public void enqueue(Callback responseCallback) {
        synchronized (this) {
            if (executed) throw new IllegalStateException("Already Executed");
            executed = true;
        }
        client.dispatcher().enqueue(new AsyncCall(responseCallback));
    }

調用了上面我們沒有詳細說的 Dispatcher 類中的 enqueue(Call ) 方法.接著繼續看:

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

如果中的 runningAsynCalls 不滿,且 call 占用的 host 小于最大數量,則將 call 加入到 runningAsyncCalls 中執行,同時利用線程池執行 call ;否者將 call 加入到 readyAsyncCalls 中。 runningAsyncCalls 和 readyAsyncCalls 是什么呢?看下面:

/** Ready async calls in the order they'll be run. */
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>(); //正在準備中的異步請求隊列

/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>(); //運行中的異步請求

/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>(); //同步請求

call 加入到線程池中執行了。現在再看 AsynCall的 代碼,它是 RealCall 中的內部類:

//異步請求
    final class AsyncCall extends NamedRunnable {
        private final Callback responseCallback;

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

AysncCall 中的 execute() 中的方法,同樣是通過 Response response = getResponseWithInterceptorChain(); 來獲得response,這樣異步任務也同樣通過了interceptor,剩下的流程就和上面一樣了。

結語:

看到這,不知道你是否明白了OkHttp的請求過程,如果有什么問題或意見,歡迎私信。

參考

  1. OkHttp 官方教程解析 - 徹底入門 OkHttp 使用
  2. 拆輪子系列:拆 OkHttp

 

來自:http://www.jianshu.com/p/27c1554b7fee

 

 本文由用戶 rbr008 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!