okHttp如何使用及調(diào)用流程源碼分析

1. okHttp的使用

  • 1.1 構(gòu)造并配置okHttpClient

OkHttpClient okHttpClient = new OkHttpClient();

然后看下配置項(xiàng):

  final Dispatcher dispatcher;//調(diào)度器,線程管理
  final @Nullable Proxy proxy;//代理
  final List<Protocol> protocols;//協(xié)議(如Http1.1夺英,Http2)
  final List<ConnectionSpec> connectionSpecs;//傳輸層版本和連接協(xié)議
  final List<Interceptor> interceptors;//攔截器
  final List<Interceptor> networkInterceptors;//網(wǎng)絡(luò)攔截器
  final EventListener.Factory eventListenerFactory;
  final ProxySelector proxySelector;//代理選擇器
  final CookieJar cookieJar;//cookie
  final @Nullable Cache cache;//緩存
  final @Nullable InternalCache internalCache;//內(nèi)部緩存
  final SocketFactory socketFactory;//socket工廠
  final @Nullable SSLSocketFactory sslSocketFactory;//安全套層socket工廠撩独,用于https
  final @Nullable CertificateChainCleaner certificateChainCleaner;//驗(yàn)證確認(rèn)響應(yīng)證書
  final HostnameVerifier hostnameVerifier;//主機(jī)名確認(rèn)
  final CertificatePinner certificatePinner;//用于自簽名證書
  final Authenticator proxyAuthenticator;//代理身份驗(yàn)證
  final Authenticator authenticator;//本地身份驗(yàn)證
  final ConnectionPool connectionPool;/連接池
  final Dns dns;//dns
  final boolean followSslRedirects;/安全套接層重定向
  final boolean followRedirects;//本地重定向(http和https互相跳轉(zhuǎn)時(shí)媒抠,默認(rèn)為true)
  final boolean retryOnConnectionFailure;//連接失敗重試
  final int connectTimeout;//連接超時(shí)時(shí)間
  final int readTimeout;//讀取超時(shí)時(shí)間
  final int writeTimeout;//寫入超時(shí)時(shí)間
  final int pingInterval;//ping的間隔

其實(shí)構(gòu)建OkHttpClient時(shí)okHttp已經(jīng)幫我們實(shí)現(xiàn)了默認(rèn)配置牧愁,我們可以根據(jù)需要進(jìn)行修改毅否。

 OkHttpClient.Builder okHttpBuilder = new OkHttpClient.Builder();
        okHttpBuilder.addInterceptor(...);
        okHttpBuilder.cache(...);
        OkHttpClient okHttpClient = okHttpBuilder.build();
  • 1.2 構(gòu)造并配置Request

    Request.Builder builder = new Request.Builder(); 
    
    builder.url("http://v.juhe.cn/"); 
    
    Request request = builder.build(); 
    

    這里只配置了url,再看下其他的配置項(xiàng):

    public Builder() {
          this.method = "GET";
          this.headers = new Headers.Builder();
        }
    
        Builder(Request request) {
          this.url = request.url;
          this.method = request.method;
          this.body = request.body;
          this.tag = request.tag;
          this.headers = request.headers.newBuilder();
        }
    

    可以看到如果不設(shè)置的話玄渗,默認(rèn)是GET請(qǐng)求碉咆,同時(shí)還可以設(shè)置body,tag,headers等信息骏庸。

  • 1.3 構(gòu)造Call并執(zhí)行網(wǎng)絡(luò)請(qǐng)求

     Call call = okHttpClient.newCall(request);
            call.enqueue(new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
    
                }
    
                @Override
                public void onResponse(Call call, Response response) throws IOException {
                  
                }
            });
    

    以上就是okHttp等基本使用(一般結(jié)合Retrofit,效果更佳)敌土。

2. okHttp的調(diào)用流程源碼分析

  • 2.1 從使用中可以看到首先通過newCall()方法構(gòu)造了一個(gè)Call,那么看下newCall():

     @Override public Call newCall(Request request) {
        return new RealCall(this, request, false);
      }
    

    newCall()實(shí)際返回了一個(gè)RealCall,那么執(zhí)行的enqueue()就是RealCall的enqueue():

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

    可以看到在RealCall的enqueue()中實(shí)際執(zhí)行了dispatcher的enqueue(AsyncCall(responseCallback))压彭,并傳入了AsyncCall,dispatcher其實(shí)是一個(gè)調(diào)度器菌羽,用來調(diào)度請(qǐng)求網(wǎng)絡(luò)的線程掠械。看一下它的enqueue():

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

    從上面代碼可以看到注祖,先判斷正在執(zhí)行的任務(wù)是否超過最大請(qǐng)求數(shù)猾蒂,如果超了就先存進(jìn)去。是晨,如果沒超則使用ExecutorService直接執(zhí)行肚菠,再看執(zhí)行的這個(gè)call,它是一個(gè)AsyncCall,AsyncCall繼承了NamedRunnable,NamedRunnable則繼承了Runnable罩缴,所以AsyncCall是一個(gè)Runnable蚊逢,同時(shí)NamedRunnable的run()執(zhí)行了execute(),AsyncCall重寫了execute()箫章,所以最后會(huì)執(zhí)行到AsyncCall的execute()里:

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

    可以看到getResponseWithInterceptorChain()返回了請(qǐng)求結(jié)果烙荷,然后就通過回調(diào)返回給調(diào)用端了,這個(gè)就是大概的調(diào)用流程炉抒。

    那么這個(gè)getResponseWithInterceptorChain()其實(shí)就完成了請(qǐng)求前的數(shù)據(jù)處理奢讨,建立連接,對(duì)返回?cái)?shù)據(jù)進(jìn)行處理等操作焰薄。那么這個(gè)getResponseWithInterceptorChain()其實(shí)就是整個(gè)okHttp的重點(diǎn)拿诸。

  • 2.2 看一下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);
        return chain.proceed(originalRequest);
      }
    

    可以看到這個(gè)方法里添加了一系列的Interceptor,然后把Interceptor集合傳遞給RealInterceptorChain塞茅,構(gòu)建了一個(gè)Chain亩码。最后執(zhí)行了chain.proceed(),這里使用了責(zé)任鏈模式野瘦,它的執(zhí)行流程大概是這樣的:

    Interceptor最重要的就是它的intercept()方法描沟,所有核心邏輯都在這里。

image-20191113010116100.png

先執(zhí)行第一個(gè)Interceptor的前置邏輯鞭光,然后執(zhí)行chain.proceed()吏廉,把任務(wù)轉(zhuǎn)交給下一個(gè)Interceptor,下一個(gè)Interceptor同樣執(zhí)行它的前置邏輯惰许,完成后通過chain.proceed()把任務(wù)繼續(xù)傳遞下去席覆,直到最后一個(gè)Interceptor。最后一個(gè)Interceptor獲取到結(jié)果后返回到上一個(gè)Interceptor汹买,執(zhí)行它的后置邏輯佩伤,執(zhí)行完后再返回給上一次聊倔,最后返回結(jié)果給getResponseWithInterceptorChain()。

總結(jié)一下:當(dāng)我們發(fā)起一個(gè)請(qǐng)求后生巡,當(dāng)來到getResponseWithInterceptorChain()時(shí)耙蔑,它添加了一系列Interceptor,構(gòu)成一個(gè)責(zé)任鏈孤荣。每一層的Interceptor通過前置邏輯對(duì)請(qǐng)求的原始數(shù)據(jù)(request)進(jìn)行加工甸陌,然后再通過chain.proceed()把任務(wù)傳遞給下一層,當(dāng)傳遞到最后一個(gè)Interceptor時(shí)會(huì)返回請(qǐng)求的結(jié)果(response)垃环,然后再逐層返回邀层,通過每一層的后置邏輯對(duì)結(jié)果(response)進(jìn)行處理,最后返回給請(qǐng)求端遂庄。

所以我們可以通過添加自定義Interceptor的方式寥院,在請(qǐng)求前通過前置邏輯對(duì)請(qǐng)求request進(jìn)行修改,如添加header等涛目。等返回結(jié)果后秸谢,通過后置邏輯對(duì)返回?cái)?shù)據(jù)response進(jìn)行處理。OkHttp通過OkHttpClient.Builder的addInterceptor()和addNetworkInterceptor()支持了添加自定義Interceptor霹肝,后面會(huì)說到估蹄。

  • 2.3 那么再看一下每個(gè)Interceptor具體都做了什么

    • 2.3.1 添加自定義的Interceptor

      interceptors.addAll(client.interceptors());
      

      可以看到,第一步其實(shí)是添加了我們自定義的所有的Interceptor沫换,一般來說可以添加一些信息如:

       builder.addInterceptor(new Interceptor() {
                      @Override
                      public Response intercept(Chain chain) throws IOException {
                                            //前置邏輯
                          Request request = chain.request().newBuilder()
                                  .addHeader("Content-Type", ...)
                                  .addHeader("Date", ...)
                                  .addHeader("User-Agent", ...)
                                  .addHeader("uid",...)
                                  .build();
                            //傳遞任務(wù)給下一層
                          Response response = chain.proceed(request);
                          //后置邏輯臭蚁,這里已經(jīng)拿到了請(qǐng)求結(jié)果,可以根據(jù)需要對(duì)結(jié)果直接進(jìn)行處理
                          return response;
                      }
                  });
      
    • 2.3.2 添加RetryAndFollowUpInterceptor

      interceptors.add(retryAndFollowUpInterceptor);
      

      從名字可以看出這是一個(gè)重試的Interceptor,再看它的intercept():

      //篇幅原因讯赏,省略了很多代碼
      @Override public Response intercept(Chain chain) throws IOException {
          Request request = chain.request();
          //前置邏輯垮兑,初始化連接對(duì)象
          streamAllocation = new StreamAllocation(
              client.connectionPool(), createAddress(request.url()), callStackTrace);
      
          while (true) {
          //是否已取消了連接
            if (canceled) {
              streamAllocation.release();
              throw new IOException("Canceled");
            }
      
            try {
            //移交任務(wù),等待結(jié)果
              response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);
              releaseConnection = false;
            } catch (RouteException e) {
              //路由出錯(cuò)漱挎,判斷是否進(jìn)行重試
              if (!recover(e.getLastConnectException(), false, request)) {
                throw e.getLastConnectException();
              }
            } 
      
            Request followUp = followUpRequest(response);
             //如果followUp返回空系枪,則代表請(qǐng)求成功,直接結(jié)束循環(huán)磕谅,如果請(qǐng)求失敗私爷,那么生成新的request在條件符合的時(shí)候去重試
            if (followUp == null) {
              if (!forWebSocket) {
                streamAllocation.release();
              }
              return response;
            }
        }
      
      

      請(qǐng)注意,這里省略了很多代碼膊夹,判斷條件不是全部衬浑。之后的Interceptor同樣會(huì)省略,不再提示

      首先RetryAndFollowUpInterceptor的前置邏輯是初始化了一個(gè)連接對(duì)象StreamAllocation放刨,然后判斷是否取消了請(qǐng)求嚎卫,請(qǐng)求過程中是否路由出錯(cuò),取消或出錯(cuò)的話就直接拋出異常,否則執(zhí)行proceed()傳遞請(qǐng)求拓诸,然后等待請(qǐng)求結(jié)果。請(qǐng)求結(jié)果返回后麻昼,執(zhí)行后置邏輯奠支,根據(jù)設(shè)置的條件對(duì)結(jié)果進(jìn)行判斷,看是否需要去重試抚芦,如果需要?jiǎng)t再次進(jìn)入while()循環(huán)進(jìn)行請(qǐng)求倍谜,是否需要進(jìn)行重定向,都不需要?jiǎng)t直接返回結(jié)果叉抡。(注意尔崔!這里省略了一些代碼)

    • 2.3.3 添加BridgeInterceptor

      interceptors.add(new BridgeInterceptor(client.cookieJar()));
      

      可以看到,這一步添加了BridgeInterceptor褥民,并且傳入了CookieJar季春,那么意味著,可以自定義自己的cookieJar:

        OkHttpClient okHttpClient = okHttpBuilder
                     .cookieJar(new CookieJar() {
                         @Override
                         public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
                             //保存cookie
                         }
      
                         @Override
                         public List<Cookie> loadForRequest(HttpUrl url) {
                                 //加載cookie
                             return null;
                         }
                     })
                      .build();
      

      再看它的intercept():

      @Override public Response intercept(Chain chain) throws IOException {
          Request userRequest = chain.request();
          Request.Builder requestBuilder = userRequest.newBuilder();
      
          RequestBody body = userRequest.body();
          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.
          boolean transparentGzip = false;
          if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
            transparentGzip = true;
            //添加gzip壓縮格式
            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());
      
          HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());
      
          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();
        }
      

      首先BridgeInterceptor的前置邏輯對(duì)請(qǐng)求的header進(jìn)行了設(shè)置消返,調(diào)用者設(shè)置過的就直接使用载弄,沒設(shè)置過的則設(shè)置一個(gè)默認(rèn)的。然后還添加了gzip壓縮的支持撵颊。然后執(zhí)行proceed()傳遞請(qǐng)求宇攻,等待請(qǐng)求結(jié)果。請(qǐng)求結(jié)果返回后倡勇,執(zhí)行后置邏輯逞刷,這里的后置邏輯主要就是對(duì)數(shù)據(jù)進(jìn)行了解壓縮后返回。

    • 2.3.4 添加CacheInterceptor

      interceptors.add(new CacheInterceptor(client.internalCache()));
      

      從名字可以看出它是一個(gè)緩存的Interceptor妻熊,并且傳入了Cache,那么意味著可以自定義Cache:

       File cacheFile = new File(...);
       Cache cache = new Cache(cacheFile, ...);
       OkHttpClient okHttpClient = okHttpBuilder
                      .cache(cache)
                      .build();
      

      再看它的intercept():

      //篇幅原因夸浅,省略了很多代碼
      @Override public Response intercept(Chain chain) throws IOException {
            //創(chuàng)建緩存策略
          CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
          
          // If we're forbidden from using the network and the cache is insufficient, fail.
          //如果網(wǎng)絡(luò)有問題,同時(shí)沒有緩存固耘,就構(gòu)建并返回一個(gè)code是504的response
          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(Util.EMPTY_RESPONSE)
                .sentRequestAtMillis(-1L)
                .receivedResponseAtMillis(System.currentTimeMillis())
                .build();
          }
      
          // If we don't need the network, we're done.
          //如果網(wǎng)絡(luò)有問題题篷,則使用緩存構(gòu)建并返回一個(gè)response
          if (networkRequest == null) {
            return cacheResponse.newBuilder()
                .cacheResponse(stripBody(cacheResponse))
                .build();
          }
      
          Response networkResponse =networkResponse = chain.proceed(networkRequest);
      
          if (cacheResponse != null) {
          //服務(wù)器返回304,代表內(nèi)容沒有更新厅目,繼續(xù)使用緩存
            if (networkResponse.code() == HTTP_NOT_MODIFIED) {
              Response response = cacheResponse.newBuilder()
                  .headers(combine(cacheResponse.headers(), networkResponse.headers()))
                  .sentRequestAtMillis(networkResponse.sentRequestAtMillis())
                  .receivedResponseAtMillis(networkResponse.receivedResponseAtMillis())
                  .cacheResponse(stripBody(cacheResponse))
                  .networkResponse(stripBody(networkResponse))
                  .build();
              networkResponse.body().close();
              return response;
            } else {
              closeQuietly(cacheResponse.body());
            }
          }
          //讀取網(wǎng)路結(jié)果
          Response response = networkResponse.newBuilder()
              .cacheResponse(stripBody(cacheResponse))
              .networkResponse(stripBody(networkResponse))
              .build();
          //進(jìn)行緩存
          if (cache != null) {
              CacheRequest cacheRequest = cache.put(response);
              return cacheWritingResponse(cacheRequest, response);
            }
          }
          //返回結(jié)果
          return response;
        }
      

      首先CacheInterceptor的前置邏輯創(chuàng)建了一個(gè)緩存策略番枚,然后判斷網(wǎng)絡(luò)是否有問題,有問題的話是否有緩存损敷,有緩存直接返回緩存葫笼,沒有緩存的話則則直接返回一個(gè)帶錯(cuò)誤信息的Response,不再往下執(zhí)行。如果有網(wǎng)絡(luò)也沒緩存拗馒,那么執(zhí)行proceed()傳遞請(qǐng)求路星,等待結(jié)果。返回結(jié)果后,在后置邏輯中洋丐,如果結(jié)果返回304則代表服務(wù)器沒有更新數(shù)據(jù)呈昔,直接返回緩存。如果正常返回?cái)?shù)據(jù)則讀取結(jié)果后對(duì)結(jié)果進(jìn)行緩存后返回友绝。

    • 2.3.5 添加ConnectInterceptor

      interceptors.add(new ConnectInterceptor(client));
      

      從名字可以看出這是一個(gè)連接的Interceptor堤尾,再看它的intercept():

      @Override public Response intercept(Chain chain) throws IOException {
          RealInterceptorChain realChain = (RealInterceptorChain) chain;
          Request request = realChain.request();
            //獲取連接對(duì)象
          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);
        }
      

      ConnectInterceptor的intercept()中首先獲取了之前構(gòu)建的連接對(duì)象StreamAllocation迁客,然后建立了連接郭宝,生成了編碼解碼器,獲取了連接后執(zhí)行 realChain.proceed(request, streamAllocation, httpCodec, connection)掷漱,傳遞請(qǐng)求給下一層粘室,同時(shí)直接返回結(jié)果,沒有后置邏輯卜范。

    • 2.3.6 添加networkInterceptors

      interceptors.addAll(client.networkInterceptors());
      

      可以看到衔统,這個(gè)networkInterceptors位置是在建立連接之后,寫數(shù)據(jù)以及獲取數(shù)據(jù)之前先朦,所以它一般是對(duì)發(fā)出的請(qǐng)求做最后的處理以及拿到數(shù)據(jù)后最初的處理缰冤,如使用Stetho抓包時(shí)可以添加:

      builder.addNetworkInterceptor(new StethoInterceptor());
      
    • 2.3.7 添加CallServerInterceptor

      interceptors.add(new CallServerInterceptor(forWebSocket));
      

      看一下它的intercept():

      @Override 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();
          //寫頭信息
          httpCodec.writeRequestHeaders(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();
              responseBuilder = httpCodec.readResponseHeaders(true);
            }
      
            if (responseBuilder == null) {
              // Write the request body if the "Expect: 100-continue" expectation was met.
              Sink requestBodyOut = httpCodec.createRequestBody(request, request.body().contentLength());
              BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
              request.body().writeTo(bufferedRequestBody);
              bufferedRequestBody.close();
            } 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) {
            responseBuilder = httpCodec.readResponseHeaders(false);
          }
          //構(gòu)建response 
          Response response = responseBuilder
              .request(request)
              .handshake(streamAllocation.connection().handshake())
              .sentRequestAtMillis(sentRequestMillis)
              .receivedResponseAtMillis(System.currentTimeMillis())
              .build();
      
          int code = response.code();
          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 {
              //獲取body信息
            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;
        }
      

      這個(gè)Interceptor是最后一個(gè),連接之前的ConnectInterceptor已經(jīng)建立喳魏,這里就只需要寫數(shù)據(jù)和讀數(shù)據(jù)就行了棉浸,可以看到writeRequestHeaders()先寫了頭信息,之后再寫body等刺彩。然后等數(shù)據(jù)返回后讀取迷郑,然后返回給上一層。由于它是最后一個(gè)Interceptor所以它不需要再傳遞請(qǐng)求创倔,直接返回就OK了嗡害。

3. okHttp調(diào)用流程總結(jié)

  1. 構(gòu)建okHttpClient,并添加需要的配置畦攘。如添加自定義的Interceptor霸妹,是否重連,設(shè)置超時(shí)時(shí)間知押,Cookie叹螟,Cache等。

  2. 配置Request台盯,如請(qǐng)求方法罢绽,請(qǐng)求頭,請(qǐng)求體等静盅。

  3. 通過okHttpClient.newCall(request)生成Call對(duì)象良价,這個(gè)Call實(shí)際是RealCall。

  4. 執(zhí)行它的enqueue(),最后實(shí)際會(huì)執(zhí)行到AsyncCall的execute()中,然后執(zhí)行它的getResponseWithInterceptorChain()等待返回結(jié)果明垢。

    在getResponseWithInterceptorChain()中使用了責(zé)任鏈模式蚣常,每一個(gè)節(jié)點(diǎn)是一個(gè)interceptor,在哦它的interceptors()中:添加前置邏輯袖外,然后調(diào)用proceed方法史隆,傳遞請(qǐng)求到下一層,等待結(jié)果返回曼验,直達(dá)最后網(wǎng)絡(luò)請(qǐng)求完成返回?cái)?shù)據(jù),又原路返回執(zhí)行每一個(gè)interceptors的后置邏輯粘姜。

  5. getResponseWithInterceptorChain()首先執(zhí)行了自定義的Interceptor的前置邏輯鬓照,它獲取了用戶設(shè)置的自定義配置并添加到請(qǐng)求中,然后轉(zhuǎn)交給下一層孤紧,并等待結(jié)果返回豺裆。

  6. 第二個(gè)執(zhí)行了RetryAndFollowUpInterceptor的前置邏輯,它初始化了連接對(duì)象号显,如果沒有取消請(qǐng)求臭猜,則繼續(xù)轉(zhuǎn)交請(qǐng)求給下一層,并等待結(jié)果返回押蚤。

  7. 第三個(gè)執(zhí)行了BridgeInterceptor的前置邏輯蔑歌,它處理了header信息,并添加了gzip支持揽碘。然后轉(zhuǎn)交請(qǐng)求給下一層次屠,并等待結(jié)果返回。

  8. 第四個(gè)執(zhí)行了CacheInterceptor的前置邏輯雳刺,它創(chuàng)建了緩存策略劫灶,如果可以返回緩存,或網(wǎng)絡(luò)有問題這直接返回掖桦。否則轉(zhuǎn)交請(qǐng)求給下一層本昏,并等待結(jié)果返回。

  9. 第五個(gè)執(zhí)行了ConnectInterceptor的前置邏輯枪汪,它構(gòu)建并建立了連接涌穆,然后轉(zhuǎn)交請(qǐng)求給下一層,并等待結(jié)果返回料饥。

  10. 第五個(gè)執(zhí)行了networkInterceptors的前置邏輯蒲犬,這里如果設(shè)置了,那么可以在請(qǐng)求發(fā)出前做最后的處理岸啡,然后轉(zhuǎn)交請(qǐng)求給下一層原叮,并等待結(jié)果返回。

  11. 第五個(gè)執(zhí)行了CallServerInterceptor的前置邏輯,這是最后一個(gè)Interceptor奋隶,它已經(jīng)拿到了客戶端設(shè)置的請(qǐng)求的所有配置擂送,并且連接已經(jīng)建立。在這里正式開始往網(wǎng)絡(luò)寫請(qǐng)求唯欣,并等待返回請(qǐng)求結(jié)果嘹吨。返回結(jié)果進(jìn)行一定的判斷處理后直接返回給上一層。

  12. 如果設(shè)置了networkInterceptors境氢,那么結(jié)果先在networkInterceptors的后置邏輯處理后再返回給ConnectInterceptor

  13. ConnectInterceptor沒有后置邏輯蟀拷,直接返回給CacheInterceptor

  14. CacheInterceptor的后置邏輯對(duì)返回結(jié)果進(jìn)行緩存后,然后返回給BridgeInterceptor

  15. BridgeInterceptor的后置邏輯對(duì)返回結(jié)果進(jìn)行解壓縮萍聊,然后返回給RetryAndFollowUpInterceptor

  16. RetryAndFollowUpInterceptor的后置邏輯對(duì)返回結(jié)果進(jìn)行判斷问芬,是否需要重定向,是否需要執(zhí)行重試邏輯寿桨,不需要?jiǎng)t返回請(qǐng)求結(jié)果

  17. 如果設(shè)置了自定義Interceptor此衅,那么會(huì)返回到這里,根據(jù)需求對(duì)結(jié)果進(jìn)行處理亭螟,然后最終返回給調(diào)用端挡鞍。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市预烙,隨后出現(xiàn)的幾起案子墨微,更是在濱河造成了極大的恐慌,老刑警劉巖默伍,帶你破解...
    沈念sama閱讀 218,525評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件欢嘿,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡也糊,警方通過查閱死者的電腦和手機(jī)炼蹦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來狸剃,“玉大人掐隐,你說我怎么就攤上這事〕伲” “怎么了虑省?”我有些...
    開封第一講書人閱讀 164,862評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)僧凰。 經(jīng)常有香客問我探颈,道長(zhǎng),這世上最難降的妖魔是什么训措? 我笑而不...
    開封第一講書人閱讀 58,728評(píng)論 1 294
  • 正文 為了忘掉前任伪节,我火速辦了婚禮光羞,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘怀大。我一直安慰自己纱兑,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,743評(píng)論 6 392
  • 文/花漫 我一把揭開白布化借。 她就那樣靜靜地躺著潜慎,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蓖康。 梳的紋絲不亂的頭發(fā)上铐炫,一...
    開封第一講書人閱讀 51,590評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音蒜焊,去河邊找鬼驳遵。 笑死,一個(gè)胖子當(dāng)著我的面吹牛山涡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播唆迁,決...
    沈念sama閱讀 40,330評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼鸭丛,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了唐责?” 一聲冷哼從身側(cè)響起鳞溉,我...
    開封第一講書人閱讀 39,244評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎鼠哥,沒想到半個(gè)月后熟菲,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,693評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡朴恳,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,885評(píng)論 3 336
  • 正文 我和宋清朗相戀三年抄罕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片于颖。...
    茶點(diǎn)故事閱讀 40,001評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡呆贿,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出森渐,到底是詐尸還是另有隱情做入,我是刑警寧澤,帶...
    沈念sama閱讀 35,723評(píng)論 5 346
  • 正文 年R本政府宣布同衣,位于F島的核電站竟块,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏耐齐。R本人自食惡果不足惜浪秘,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,343評(píng)論 3 330
  • 文/蒙蒙 一蒋情、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧秫逝,春花似錦恕出、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至刷后,卻和暖如春的畴,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背尝胆。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工丧裁, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人含衔。 一個(gè)月前我還...
    沈念sama閱讀 48,191評(píng)論 3 370
  • 正文 我出身青樓煎娇,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親贪染。 傳聞我的和親對(duì)象是個(gè)殘疾皇子缓呛,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,955評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 1 介紹 在我們所處的互聯(lián)網(wǎng)世界中,HTTP協(xié)議算得上是使用最廣泛的網(wǎng)絡(luò)協(xié)議杭隙。OKHttp是一款高效的HTTP客戶...
    天才木木閱讀 5,727評(píng)論 7 53
  • 這篇文章主要講 Android 網(wǎng)絡(luò)請(qǐng)求時(shí)所使用到的各個(gè)請(qǐng)求庫(kù)的關(guān)系哟绊,以及 OkHttp3 的介紹。(如理解有誤痰憎,...
    小莊bb閱讀 1,159評(píng)論 0 4
  • 那么我今天給大家簡(jiǎn)單地講一下Okhttp這款網(wǎng)絡(luò)框架及其原理票髓。它是如何請(qǐng)求數(shù)據(jù),如何響應(yīng)數(shù)據(jù)的 有什么優(yōu)點(diǎn)铣耘?它的應(yīng)...
    卓而不群_0137閱讀 316評(píng)論 0 1
  • 緩存的一般思路 下面是我理解的網(wǎng)絡(luò)請(qǐng)求框架的緩存基本實(shí)現(xiàn)洽沟。大致的過程是有緩存用緩存的數(shù)據(jù),沒緩存發(fā)起http請(qǐng)求取...
    原件閱讀 2,809評(píng)論 3 12
  • 老同學(xué)們涡拘,今天是我們告別八中42周年的紀(jì)念日×崆現(xiàn)在想來就象是在昨天。人生苦短轉(zhuǎn)眼就是幾十年鳄乏,42年對(duì)壽命長(zhǎng)的來說己...
    山東馬哥閱讀 193評(píng)論 0 2