OkHttp解析(一)從用法看清原理

OkHttp解析系列

OkHttp解析(一)從用法看清原理
OkHttp解析(二)網(wǎng)絡(luò)連接
OkHttp解析(三)關(guān)于Okio

認(rèn)識(shí)使用


一系列是對(duì)OkHttp的源碼解析,就從大家使用方法入手
常用方法

    OkHttpClient okHttpClient =  new OkHttpClient.Builder().connectTimeout(10, TimeUnit.SECONDS) .cookieJar(new CookieJar() {
                    @Override
                    public void saveFromResponse(HttpUrl url, List<Cookie> cookies) 
                    {
                        //...
                    }

                    @Override
                    public List<Cookie> loadForRequest(HttpUrl url) {
                        //...
                        return null;
                    }
                }).build();
        Request request = new Request.Builder().url("www.baidu.com").build();
        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 {

            }
        });
        //同步
        try {
            Response response =  call.execute();
        } catch (IOException e) {
            e.printStackTrace();
        }

可以看到常用的用法禽拔,是創(chuàng)建OKHttpClient對(duì)象优烧,構(gòu)建請(qǐng)求Request躏惋,請(qǐng)求調(diào)用Call耻陕,而Call里面對(duì)應(yīng)有同步異步兩種方法,同步下則要在線程中去進(jìn)行恰矩。這就是大家常用的方法,我們先來(lái)看看OkHttpClient

OkHttpClient


我們先看注釋說(shuō)明


/**
 * Factory for {@linkplain Call calls}, which can be used to send HTTP requests and read their responses.
 OkHttpClient算是執(zhí)行調(diào)用請(qǐng)求Call的工廠谦去,這個(gè)工廠將會(huì)被用來(lái)發(fā)送Http請(qǐng)求和讀取他們的返回
 *
 * ...
 *
 * <p>OkHttp performs best when you create a single {@code OkHttpClient} instance and reuse it for all of your HTTP calls. This is because each client holds its own connection pool and thread pools. Reusing connections and threads reduces latency and saves memory. Conversely, creating a client for each request wastes resources on idle pools.
 這里強(qiáng)調(diào)OkHttp的使用最好創(chuàng)建一個(gè)單例OkHttpClient實(shí)例慷丽,并且重復(fù)使用。這是因?yàn)槊恳粋€(gè)Client都有自己的一個(gè)連接池connection pool和線程池thread pools鳄哭。重用這些連接池和線程池可以減少延遲和節(jié)約內(nèi)存要糊。
 *
   ...
 *
 * <p>Or use {@code new OkHttpClient.Builder()} to create a shared instance with custom settings:
 * <pre>   {@code
 * 創(chuàng)建實(shí)例說(shuō)明,可以看到是通過(guò)Builder模式創(chuàng)建的
 *   // The singleton HTTP client.
 *   public final OkHttpClient client = new OkHttpClient.Builder()
 *       .addInterceptor(new HttpLoggingInterceptor())
 *       .cache(new Cache(cacheDir, cacheSize))
 *       .build();
 * }</pre>
 *
 * <h3>Customize your client with newBuilder()</h3>
 *
 * 可以調(diào)用newBuilder方法來(lái)定制自己的client妆丘,調(diào)用后創(chuàng)建的client會(huì)保存上次的連接池和線程池以及之前一些配置
 * <p>You can customize a shared OkHttpClient instance with {@link #newBuilder()}. This builds a client that shares the same connection pool, thread pools, and configuration. Use the builder methods to configure the derived client for a specific purpose.
 *
 * <p>This example shows a call with a short 500 millisecond timeout: <pre>   {@code
 *
 *   OkHttpClient eagerClient = client.newBuilder()
 *       .readTimeout(500, TimeUnit.MILLISECONDS)
 *       .build();
 *   Response response = eagerClient.newCall(request).execute();
 * }</pre>
 *
 * ...
 */

OkHttpClient算是執(zhí)行調(diào)用請(qǐng)求Call的工廠锄俄,這個(gè)工廠將會(huì)被用來(lái)發(fā)送Http請(qǐng)求和讀取他們的返回這里強(qiáng)調(diào)OkHttp的使用最好創(chuàng)建一個(gè)單例OkHttpClient實(shí)例,并且重復(fù)使用勺拣。這是因?yàn)槊恳粋€(gè)Client都有自己的一個(gè)連接池connection pool和線程池thread pools奶赠。重用這些連接池和線程池可以減少延遲和節(jié)約內(nèi)存。

我們看下里面的源碼药有,由于使用Builder毅戈,看OkHttpClient的也就相當(dāng)于看Builder

  public static final class Builder {
    Dispatcher dispatcher; //調(diào)度器,里面包含了線程池和三個(gè)隊(duì)列(readyAsyncCalls:保存等待執(zhí)行的異步請(qǐng)求
    
    Proxy proxy; //代理類(lèi)愤惰,默認(rèn)有三種代理模式DIRECT(直連),HTTP(http代理),SOCKS(socks代理)苇经,這三種模式,折騰過(guò)科學(xué)上網(wǎng)的或多或少都了解一點(diǎn)吧宦言。
    
    List<Protocol> protocols; //協(xié)議集合扇单,協(xié)議類(lèi),用來(lái)表示使用的協(xié)議版本奠旺,比如`http/1.0,`http/1.1,`spdy/3.1,`h2等
    
    List<ConnectionSpec> connectionSpecs; //連接規(guī)范蜘澜,用于配置Socket連接層。對(duì)于HTTPS凉倚,還能配置安全傳輸層協(xié)議(TLS)版本和密碼套件
    
    final List<Interceptor> interceptors = new ArrayList<>(); //攔截器兼都,用來(lái)監(jiān)聽(tīng)請(qǐng)求
    final List<Interceptor> networkInterceptors = new ArrayList<>();
    
    ProxySelector proxySelector; //代理選擇類(lèi),默認(rèn)不使用代理稽寒,即使用直連方式,當(dāng)然趟章,我們可以自定義配置杏糙,以指定URI使用某種代理,類(lèi)似代理軟件的PAC功能蚓土。
    
    CookieJar cookieJar; //Cookie的保存獲取
    
    Cache cache; //緩存類(lèi)宏侍,內(nèi)部使用了DiskLruCache來(lái)進(jìn)行管理緩存,匹配緩存的機(jī)制不僅僅是根據(jù)url蜀漆,而且會(huì)根據(jù)請(qǐng)求方法和請(qǐng)求頭來(lái)驗(yàn)證是否可以響應(yīng)緩存谅河。此外,僅支持GET請(qǐng)求的緩存。
    
    InternalCache internalCache;  //內(nèi)置緩存
    
    SocketFactory socketFactory; //Socket的抽象創(chuàng)建工廠绷耍,通過(guò)`createSocket來(lái)創(chuàng)建Socket
    吐限。
    SSLSocketFactory sslSocketFactory; //安全套接層工廠,HTTPS相關(guān)褂始,用于創(chuàng)建SSLSocket诸典。一般配置HTTPS證書(shū)信任問(wèn)題都需要從這里著手。對(duì)于不受信任的證書(shū)一般會(huì)提示javax.net.ssl.SSLHandshakeException異常崎苗。
    
    CertificateChainCleaner certificateChainCleaner; //證書(shū)鏈清潔器狐粱,HTTPS相關(guān),用于從[Java]的TLS API構(gòu)建的原始數(shù)組中統(tǒng)計(jì)有效的證書(shū)鏈胆数,然后清除跟TLS握手不相關(guān)的證書(shū)肌蜻,提取可信任的證書(shū)以便可以受益于證書(shū)鎖機(jī)制。
    
    HostnameVerifier hostnameVerifier; //主機(jī)名驗(yàn)證器必尼,與HTTPS中的SSL相關(guān)宋欺,當(dāng)握手時(shí)如果URL的主機(jī)名不是可識(shí)別的主機(jī),就會(huì)要求進(jìn)行主機(jī)名驗(yàn)證
    
    CertificatePinner certificatePinner; // 證書(shū)鎖胰伍,HTTPS相關(guān)齿诞,用于約束哪些證書(shū)可以被信任,可以防止一些已知或未知的中間證書(shū)機(jī)構(gòu)帶來(lái)的攻擊行為骂租。如果所有證書(shū)都不被信任將拋出SSLPeerUnverifiedException異常祷杈。
    
    Authenticator proxyAuthenticator; //身份認(rèn)證器,當(dāng)連接提示未授權(quán)時(shí)渗饮,可以通過(guò)重新設(shè)置請(qǐng)求頭來(lái)響應(yīng)一個(gè)新的Request但汞。狀態(tài)碼401表示遠(yuǎn)程服務(wù)器請(qǐng)求授權(quán),407表示代理服務(wù)器請(qǐng)求授權(quán)互站。該認(rèn)證器在需要時(shí)會(huì)被RetryAndFollowUpInterceptor觸發(fā)私蕾。
    
    Authenticator authenticator;
    ConnectionPool connectionPool; //連接池
    Dns dns;
    boolean followSslRedirects; //是否遵循SSL重定向
    boolean followRedirects; //是否重定向
    boolean retryOnConnectionFailure; //失敗是否重新連接
    int connectTimeout; //連接超時(shí)
    int readTimeout; //讀取超時(shí)
    int writeTimeout; //寫(xiě)入超時(shí)
    ...
  }

注釋中說(shuō)明的很清楚了
可以看到在OkHttpClient這里就設(shè)置了這么多的字段,常用的讀寫(xiě)時(shí)間胡桃,延遲請(qǐng)求踩叭,緩存都在這里設(shè)置了。
看下構(gòu)造器的賦值

    public Builder() {
      dispatcher = new Dispatcher();
      protocols = DEFAULT_PROTOCOLS; //默認(rèn)支持的協(xié)議
      connectionSpecs = DEFAULT_CONNECTION_SPECS; //默認(rèn)的連接規(guī)范
      proxySelector = ProxySelector.getDefault(); //默認(rèn)的代理選擇器翠胰,直連
      cookieJar = CookieJar.NO_COOKIES; //默認(rèn)不進(jìn)行管理Cookie
      socketFactory = SocketFactory.getDefault();
      hostnameVerifier = OkHostnameVerifier.INSTANCE; //主機(jī)驗(yàn)證
      certificatePinner = CertificatePinner.DEFAULT; //證書(shū)鎖容贝,默認(rèn)不開(kāi)啟
      proxyAuthenticator = Authenticator.NONE; //默認(rèn)不進(jìn)行授權(quán)
      authenticator = Authenticator.NONE;
      connectionPool = new ConnectionPool(); //連接池
      dns = Dns.SYSTEM;
      followSslRedirects = true;
      followRedirects = true;
      retryOnConnectionFailure = true;
      //超時(shí)時(shí)間
      connectTimeout = 10_000;
      readTimeout = 10_000;
      writeTimeout = 10_000;
    }

Dispatcher

它是一個(gè)異步請(qǐng)求執(zhí)行政策,當(dāng)我們用OkHttpClient.newCall(request)進(jìn)行execute/enenqueue時(shí)之景,實(shí)際是將請(qǐng)求Call放到了Dispatcher中斤富,okhttp使用Dispatcher進(jìn)行線程分發(fā),它有兩種方法锻狗,一個(gè)是普通的同步單線程满力;另一種是使用了隊(duì)列進(jìn)行并發(fā)任務(wù)的分發(fā)(Dispatch)與回調(diào)焕参。另外,在Dispatcher中每一個(gè)請(qǐng)求都是使用 ExecutorService 來(lái)執(zhí)行的油额。

public final class Dispatcher {
  private int maxRequests = 64; //最大并發(fā)數(shù)為64叠纷,同時(shí)請(qǐng)求
  private int maxRequestsPerHost = 5; //每個(gè)主機(jī)的最大請(qǐng)求數(shù)為5
  private Runnable idleCallback; //閑置接口

  /** Executes calls. Created lazily. */
  private ExecutorService executorService; //線程池

  //緩存好的異步調(diào)用,都是放在隊(duì)列里保存
  private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>(); 

  //運(yùn)行中的異步調(diào)用悔耘,都是放在隊(duì)列里保存
  private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();

  //運(yùn)行中的同步調(diào)用讲岁,都是放在隊(duì)列里保存
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
  ...
}

前面說(shuō)到,當(dāng)我們用OkHttpClient.newCall(request)進(jìn)行execute/enqueue時(shí)衬以,實(shí)際是將請(qǐng)求Call放到了Dispatcher中缓艳。
我們先看回之前的用法

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 {

            }
        });
        //同步
        try {
            Response response =  call.execute();
        } catch (IOException e) {
            e.printStackTrace();
        }

通過(guò)調(diào)用okHttpClient.newCall將請(qǐng)求Request構(gòu)造成Call,進(jìn)行發(fā)起請(qǐng)求看峻。

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

newCall這里則是創(chuàng)建了一個(gè)RealCall對(duì)象

Call

首先對(duì)于Call阶淘,大家比較熟悉,它是一個(gè)接口互妓,定義了各種Http連接請(qǐng)求的方法

public interface Call {
  Request request(); 
  ...
  Response execute() throws IOException;
  
  void enqueue(Callback responseCallback);

  void cancel();
  
  boolean isExecuted();

  boolean isCanceled();

  interface Factory {
    Call newCall(Request request);
  }
}

可以通過(guò)request()方法獲取自己的請(qǐng)求體溪窒,調(diào)用enqueue發(fā)起異步請(qǐng)求,調(diào)用execute發(fā)起同步請(qǐng)求

RealCall

RealCall則是Call的實(shí)現(xiàn)類(lèi)

final class RealCall implements Call {
  private final OkHttpClient client;
  private final RetryAndFollowUpInterceptor retryAndFollowUpInterceptor;

  // Guarded by this.
  private boolean executed;

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

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

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

  @Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    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;
    }
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }

    ...
}

RealCall中實(shí)現(xiàn)了Execute和enqueue等方法冯勉。而在RealCall的execute和enqueue方法中都調(diào)用到了dispatcher.enqueue/execute澈蚌。

我們先看下同步方法RealCall.execute

@Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    try {
      client.dispatcher().executed(this);
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } finally {
      client.dispatcher().finished(this);
    }
  }

同步方法中做了4件事

  1. 檢查這個(gè) call 是否已經(jīng)被執(zhí)行了,每個(gè) call 只能被執(zhí)行一次灼狰,如果想要一個(gè)完全一樣的 call宛瞄,可以利用 call#clone 方法進(jìn)行克隆。
  2. 利用 client.dispatcher().executed(this) 來(lái)進(jìn)行實(shí)際執(zhí)行交胚,dispatcher 是剛才看到的OkHttpClient.Builder 的成員之一份汗,它的文檔說(shuō)自己是異步 HTTP 請(qǐng)求的執(zhí)行策略,現(xiàn)在看來(lái)蝴簇,同步請(qǐng)求它也有摻和杯活。
  3. 調(diào)用 getResponseWithInterceptorChain() 函數(shù)獲取 HTTP 返回結(jié)果,從函數(shù)名可以看出熬词,這一步還會(huì)進(jìn)行一系列“攔截”操作旁钧。
  4. 最后還要通知 dispatcher 自己已經(jīng)執(zhí)行完畢。

dispatcher 這里我們不過(guò)度關(guān)注荡澎,在同步執(zhí)行的流程中均践,涉及到 dispatcher 的內(nèi)容只不過(guò)是告知它我們的執(zhí)行狀態(tài),比如開(kāi)始執(zhí)行了(調(diào)用 executed)摩幔,比如執(zhí)行完畢了(調(diào)用 finished),在異步執(zhí)行流程中它會(huì)有更多的參與鞭铆。

這里同步請(qǐng)求或衡,只是把當(dāng)前請(qǐng)求添加到隊(duì)列而已

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

真正發(fā)出網(wǎng)絡(luò)請(qǐng)求焦影,解析返回結(jié)果的,還是 getResponseWithInterceptorChain封断,這個(gè)下面說(shuō)斯辰,最后再調(diào)用了dispatch.finish方法

  private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
    int runningCallsCount;
    Runnable idleCallback;
    synchronized (this) {
      if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
      if (promoteCalls) promoteCalls();
      runningCallsCount = runningCallsCount();
      idleCallback = this.idleCallback;
    }

    if (runningCallsCount == 0 && idleCallback != null) {
      idleCallback.run();
    }
  }

  private void promoteCalls() {
    if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
    if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.

    for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
      AsyncCall call = i.next();

      if (runningCallsForHost(call) < maxRequestsPerHost) {
        i.remove();
        runningAsyncCalls.add(call);
        executorService().execute(call);
      }

      if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
    }
  }

在finish方法中,會(huì)把當(dāng)前請(qǐng)求從running隊(duì)列中移除坡疼,然后把緩存隊(duì)列的請(qǐng)求添加到running隊(duì)列彬呻。

接下來(lái)看下異步請(qǐng)求
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));
  }

先判斷當(dāng)前Call是否在執(zhí)行,再調(diào)用dispatch.enqueue方法

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

方法中柄瑰,這里先有個(gè)判斷闸氮,如果當(dāng)前運(yùn)行的異步請(qǐng)求隊(duì)列長(zhǎng)度小于最大請(qǐng)求數(shù),也就是64,并且主機(jī)的請(qǐng)求數(shù)小于每個(gè)主機(jī)的請(qǐng)求數(shù)也就是5,則把當(dāng)前請(qǐng)求添加到 運(yùn)行隊(duì)列教沾,接著交給線程池ExecutorService處理蒲跨,否則則放置到readAsyncCall進(jìn)行緩存,等待執(zhí)行授翻。

可以看到同步與異步一點(diǎn)區(qū)別就是或悲,異步的執(zhí)行交給了線程池去操作。

我們看下OkHttp里面的線程池ExecutorService

  public synchronized ExecutorService executorService() {
    if (executorService == null) {
      executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
          new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
    }
    return executorService;
  }

這里則是通過(guò)ThreadPoolExecutor來(lái)創(chuàng)建線程池
參數(shù)說(shuō)明如下:

  • int corePoolSize: 最小并發(fā)線程數(shù)堪唐,這里并發(fā)同時(shí)包括空閑與活動(dòng)的線程巡语,如果是0的話,空閑一段時(shí)間后所有線程將全部被銷(xiāo)毀淮菠。

  • int maximumPoolSize: 最大線程數(shù)男公,當(dāng)任務(wù)進(jìn)來(lái)時(shí)可以擴(kuò)充的線程最大值,當(dāng)大于了這個(gè)值就會(huì)根據(jù)丟棄處理機(jī)制來(lái)處理

  • long keepAliveTime: 當(dāng)線程數(shù)大于corePoolSize時(shí)兜材,多余的空閑線程的最大存活時(shí)間理澎,類(lèi)似于HTTP中的Keep-alive

  • TimeUnit unit: 時(shí)間單位,一般用秒

  • BlockingQueue<Runnable> workQueue: 工作隊(duì)列曙寡,先進(jìn)先出糠爬,可以看出并不像Picasso那樣設(shè)置優(yōu)先隊(duì)列。

  • ThreadFactory threadFactory: 單個(gè)線程的工廠举庶,可以打Log执隧,設(shè)置Daemon(即當(dāng)JVM退出時(shí),線程自動(dòng)結(jié)束)等

可以看出户侥,在Okhttp中镀琉,構(gòu)建了一個(gè)閥值為[0, Integer.MAX_VALUE]的線程池,它不保留任何最小線程數(shù)蕊唐,隨時(shí)創(chuàng)建更多的線程數(shù)屋摔,當(dāng)線程空閑時(shí)只能活60秒,它使用了一個(gè)不存儲(chǔ)元素的阻塞工作隊(duì)列替梨,一個(gè)叫做"OkHttp Dispatcher"的線程工廠钓试。

也就是說(shuō)装黑,在實(shí)際運(yùn)行中,當(dāng)收到10個(gè)并發(fā)請(qǐng)求時(shí)弓熏,線程池會(huì)創(chuàng)建十個(gè)線程恋谭,當(dāng)工作完成后,線程池會(huì)在60s后相繼關(guān)閉所有線程挽鞠。

添加到線程池后就交給ThreadPoolExecutor去調(diào)用疚颊,最終則是調(diào)用到我們的請(qǐng)求AsyncCall的execute方法⌒湃希回看上面的代碼材义,異步請(qǐng)求中,我們傳遞了個(gè)Callback接口進(jìn)來(lái)狮杨,而在RealCall的enqueue方法中母截,Callback回調(diào)接口被封裝到AsyncCall中,而AsyncCall繼承與NamedRunnable橄教,而NamaedRunnable則實(shí)現(xiàn)了Runnable方法清寇。

AsyncCall

AsyncCall繼承于NamedRunnable,而NamaedRunnable則實(shí)現(xiàn)了Runnable方法

final class AsyncCall extends NamedRunnable {
    private final Callback responseCallback;

    private AsyncCall(Callback responseCallback) {
      super("OkHttp %s", redactedUrl().toString());
      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);
      }
    }
  }

可以看到在它的構(gòu)造器中护蝶,Callback就是我們?cè)O(shè)置的創(chuàng)建的华烟,帶有onFail和onResponse方法。
而線程池中最終調(diào)用到的則是我們的Runnable持灰。

這里通過(guò)

Response response = getResponseWithInterceptorChain();

方法來(lái)進(jìn)行連接訪問(wèn)盔夜,這里跟同步請(qǐng)求一樣。最后根據(jù)返回值調(diào)用callback.onFailure/onResponse

我們關(guān)鍵還是看OkHttp如何連接返回的堤魁,我們看下這個(gè)方法

private 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 (!retryAndFollowUpInterceptor.isForWebSocket()) {
      interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(
        retryAndFollowUpInterceptor.isForWebSocket()));

    Interceptor.Chain chain = new RealInterceptorChain(
        interceptors, null, null, null, 0, originalRequest);
    return chain.proceed(originalRequest);
  }

可以看到這里全都是關(guān)于Interceptor攔截器的使用

Interceptor


/**
 * Observes, modifies, and potentially short-circuits requests going out and the corresponding
 * responses coming back in. Typically interceptors add, remove, or transform headers on the request
 * or response.
 */
public interface Interceptor {
  Response intercept(Chain chain) throws IOException;

  interface Chain {
    Request request();

    Response proceed(Request request) throws IOException;

    Connection connection();
  }
}

Interceptor 是 OkHttp 最核心的一個(gè)東西喂链,不要誤以為它只負(fù)責(zé)攔截請(qǐng)求進(jìn)行一些額外的處理(例如 cookie),實(shí)際上它把實(shí)際的網(wǎng)絡(luò)請(qǐng)求妥泉、緩存椭微、透明壓縮等功能都統(tǒng)一了起來(lái),每一個(gè)功能都只是一個(gè) Interceptor盲链,它們?cè)龠B接成一個(gè) Interceptor.Chain蝇率,環(huán)環(huán)相扣,最終圓滿完成一次網(wǎng)絡(luò)請(qǐng)求刽沾。

getResponseWithInterceptorChain 函數(shù)我們可以看到本慕,Interceptor.Chain 的分布依次是:

okhttp_interceptors
okhttp_interceptors

  1. 在配置 OkHttpClient 時(shí)設(shè)置的 interceptors

  2. 負(fù)責(zé)失敗重試以及重定向的 RetryAndFollowUpInterceptor侧漓;

  3. 負(fù)責(zé)把用戶(hù)構(gòu)造的請(qǐng)求轉(zhuǎn)換為發(fā)送到服務(wù)器的請(qǐng)求锅尘、把服務(wù)器返回的響應(yīng)轉(zhuǎn)換為用戶(hù)友好的響應(yīng)的 BridgeInterceptor

  4. 負(fù)責(zé)讀取緩存直接返回布蔗、更新緩存的 CacheInterceptor鉴象;

  5. 負(fù)責(zé)和服務(wù)器建立連接的 ConnectInterceptor忙菠;

  6. 配置 OkHttpClient 時(shí)設(shè)置的 networkInterceptors何鸡;

  7. 負(fù)責(zé)向服務(wù)器發(fā)送請(qǐng)求數(shù)據(jù)纺弊、從服務(wù)器讀取響應(yīng)數(shù)據(jù)的 CallServerInterceptor

[責(zé)任鏈模式]在這個(gè) Interceptor 鏈條中得到了很好的實(shí)踐

在這里骡男,位置決定了功能淆游,最后一個(gè) Interceptor 一定是負(fù)責(zé)和服務(wù)器實(shí)際通訊的,重定向隔盛、緩存等一定是在實(shí)際通訊之前的犹菱。

對(duì)于把 Request 變成 Response 這件事來(lái)說(shuō),每個(gè) Interceptor 都可能完成這件事吮炕,所以我們循著鏈條讓每個(gè) Interceptor 自行決定能否完成任務(wù)以及怎么完成任務(wù)(自力更生或者交給下一個(gè)Interceptor)腊脱。這樣一來(lái),完成網(wǎng)絡(luò)請(qǐng)求這件事就徹底從 RealCall 類(lèi)中剝離了出來(lái)龙亲,簡(jiǎn)化了各自的責(zé)任和邏輯陕凹。

講解其他攔截器前,先認(rèn)識(shí)幾個(gè)類(lèi)

HttpStream

public interface HttpStream {
  //超時(shí)漸漸
  int DISCARD_STREAM_TIMEOUT_MILLIS = 100;

  //返回一個(gè)output stream(如果RequestBody可以轉(zhuǎn)為流)
  Sink createRequestBody(Request request, long contentLength);

  //寫(xiě)入請(qǐng)求頭
  void writeRequestHeaders(Request request) throws IOException;

  // Flush the request
  void finishRequest() throws IOException;

  //讀取請(qǐng)求頭
  Response.Builder readResponseHeaders() throws IOException;

  //返回ResponseBody
  ResponseBody openResponseBody(Response response) throws IOException;

  void cancel();
}

可以看到HttpStream是一個(gè)接口鳄炉,里面提供了很多類(lèi)似的流操作杜耙,比如Sink。

而HttpStream對(duì)應(yīng)的實(shí)現(xiàn)類(lèi)有Http1xStream拂盯、Http2xStream佑女。分別對(duì)應(yīng)HTTP/1.1、HTTP/2和SPDY協(xié)議谈竿。我們可以大約知道团驱,通過(guò)writeRequestHeaders開(kāi)始寫(xiě)入請(qǐng)求頭到服務(wù)器,createRequestBody用于獲取寫(xiě)入流來(lái)寫(xiě)入請(qǐng)求體空凸。readResponseHeaders用于讀取響應(yīng)頭嚎花,openResponseBody用于打開(kāi)一個(gè)響應(yīng)體。關(guān)于相應(yīng)實(shí)現(xiàn)的源碼這里就不分析了,比較簡(jiǎn)單劳秋,無(wú)非就是讀寫(xiě)操作吸申。

StreamAllocation

流分配器,該類(lèi)用于協(xié)調(diào)連接丛楚、流和請(qǐng)求三者之間的關(guān)系。通過(guò)調(diào)用newStream可以獲取一個(gè)HttpStream實(shí)現(xiàn)

  public HttpStream newStream(OkHttpClient client, boolean doExtensiveHealthChecks) {
    //獲取設(shè)置的超時(shí)時(shí)間
    int connectTimeout = client.connectTimeoutMillis();
    int readTimeout = client.readTimeoutMillis();
    int writeTimeout = client.writeTimeoutMillis();
    boolean connectionRetryEnabled = client.retryOnConnectionFailure();

    try {
      RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
          writeTimeout, connectionRetryEnabled, doExtensiveHealthChecks);

      HttpStream resultStream;
      if (resultConnection.framedConnection != null) {
        resultStream = new Http2xStream(client, this, resultConnection.framedConnection);
      } else {
        resultConnection.socket().setSoTimeout(readTimeout);
        resultConnection.source.timeout().timeout(readTimeout, MILLISECONDS);
        resultConnection.sink.timeout().timeout(writeTimeout, MILLISECONDS);
        resultStream = new Http1xStream(
            client, this, resultConnection.source, resultConnection.sink);
      }

      synchronized (connectionPool) {
        stream = resultStream;
        return resultStream;
      }
    } catch (IOException e) {
      throw new RouteException(e);
    }
  }

可以看到在newStream這里憔辫,通過(guò)RealConnection建立Socket連接趣些,接著獲取連接對(duì)應(yīng)的流。
而在RealConnection的connectSocket方法中

  private void connectSocket(int connectTimeout, int readTimeout) 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);

    rawSocket.setSoTimeout(readTimeout);
    try {
      Platform.get().connectSocket(rawSocket, route.socketAddress(), connectTimeout);
    } catch (ConnectException e) {
      throw new ConnectException("Failed to connect to " + route.socketAddress());
    }
    source = Okio.buffer(Okio.source(rawSocket));
    sink = Okio.buffer(Okio.sink(rawSocket));
  }

可以看到Socket和Okio的連接使用

重試與重定向攔截器 RetryAndFollowUpInterceptor

用來(lái)實(shí)現(xiàn)重試和重定向功能


  @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);
        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.
      if (priorResponse != null) {
        response = response.newBuilder()
            .priorResponse(priorResponse.newBuilder()
                .body(null)
                .build())
            .build();
      }
      //獲取重定向信息
      Request followUp = followUpRequest(response);

      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.stream() != null) {
        throw new IllegalStateException("Closing the body of " + response
            + " didn't close its backing stream. Bad interceptor?");
      }

      request = followUp;
      priorResponse = response;
    }
  }

RetryAndFollowUpInterceptor在intercept()中首先從client取得connection pool贰您,用所請(qǐng)求的URL創(chuàng)建Address對(duì)象坏平,并以此創(chuàng)建StreamAllocation對(duì)象拢操。

Address描述某一個(gè)特定的服務(wù)器地址。StreamAllocation對(duì)象則用于分配一個(gè)到特定的服務(wù)器地址的流HttpStream舶替,這個(gè)HttpStream可能是從connection pool中取得的之前沒(méi)有釋放的連接令境,也可能是重新分配的。RetryAndFollowUpInterceptor這里算是為后面的操作準(zhǔn)備執(zhí)行條件StreamAllocation顾瞪。

隨后利用Interceptor鏈中后面的Interceptor來(lái)獲取網(wǎng)絡(luò)響應(yīng)舔庶。并檢查是否為重定向響應(yīng)。若不是就將響應(yīng)返回陈醒,若是則做進(jìn)一步處理惕橙。

對(duì)于重定向的響應(yīng),RetryAndFollowUpInterceptor.intercept()會(huì)利用響應(yīng)的信息創(chuàng)建一個(gè)新的請(qǐng)求钉跷。并檢查新請(qǐng)求的服務(wù)器地址與老地址是否相同弥鹦,若不相同則會(huì)根據(jù)新的地址創(chuàng)建Address對(duì)象及StreamAllocation對(duì)象。

RetryAndFollowUpInterceptor對(duì)重定向的響應(yīng)也不會(huì)無(wú)休止的處理下去爷辙,它處理的最多的重定向級(jí)數(shù)為20次彬坏,超過(guò)20次時(shí),它會(huì)拋異常出來(lái)犬钢。

RetryAndFollowUpInterceptor通過(guò)followUpRequest()從響應(yīng)的信息中提取出重定向的信息苍鲜,接著通過(guò)sameConnection來(lái)判斷是否需要重定向連接

RetryAndFollowUpInterceptor主要做了

  • 創(chuàng)建StreamAllocation,以此傳入到后續(xù)的Interceptor中

  • 處理重定向的Http響應(yīng)

橋接攔截器 BridgeInterceptor

  @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");
      }
    }
    ...
    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中玷犹,主要用于用于完善請(qǐng)求頭混滔,比如Content-Type、Content-Length歹颓、Host坯屿、Connection、Accept-Encoding巍扛、User-Agent等等领跛,這些請(qǐng)求頭不用用戶(hù)一一設(shè)置,如果用戶(hù)沒(méi)有設(shè)置該庫(kù)會(huì)檢查并自動(dòng)完善撤奸。此外吠昭,這里會(huì)進(jìn)行加載和回調(diào)cookie。

緩存攔截器 CacheInterceptor

 @Override public Response intercept(Chain chain) throws IOException {
    //根據(jù)Request獲取緩存中的Response
    Response cacheCandidate = cache != null
        ? cache.get(chain.request())
        : null;

    long now = System.currentTimeMillis();
  //根據(jù)請(qǐng)求頭獲取用戶(hù)指定的緩存策略胧瓜,并根據(jù)緩存策略來(lái)獲取networkRequest矢棚,cacheResponse。
    CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
    Request networkRequest = strategy.networkRequest;
    Response cacheResponse = strategy.cacheResponse;

   ...

   //如果不需要網(wǎng)絡(luò)則直接返回從緩存中讀取的Response
    if (networkRequest == null) {
      return cacheResponse.newBuilder()
          .cacheResponse(stripBody(cacheResponse))
          .build();
    }
    
    Response networkResponse = null;
    try {
      networkResponse = chain.proceed(networkRequest);
    }
    ...

    // If we have a cache response too, then we're doing a conditional get.
    if (cacheResponse != null) {
      if (validate(cacheResponse, networkResponse)) {
        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()).
        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)) {
      CacheRequest cacheRequest = maybeCache(response, networkResponse.request(), cache);
      response = cacheWritingResponse(cacheRequest, response);
    }

    return response;
  }

緩存攔截器府喳,首先根據(jù)Request中獲取緩存的Response蒲肋,然后根據(jù)用于設(shè)置的緩存策略來(lái)進(jìn)一步判斷緩存的Response是否可用以及是否發(fā)送網(wǎng)絡(luò)請(qǐng)求。如果從網(wǎng)絡(luò)中讀取,此時(shí)再次根據(jù)緩存策略來(lái)決定是否緩存響應(yīng)兜粘。

這塊代碼比較多申窘,但也很直觀,主要涉及 HTTP 協(xié)議緩存細(xì)節(jié)的實(shí)現(xiàn)孔轴,而具體的緩存邏輯 OkHttp 內(nèi)置封裝了一個(gè) Cache 類(lèi)剃法,它利用 DiskLruCache,用磁盤(pán)上的有限大小空間進(jìn)行緩存距糖,按照 LRU 算法進(jìn)行緩存淘汰玄窝,這里也不再展開(kāi)。

我們可以在構(gòu)造 OkHttpClient 時(shí)設(shè)置 Cache 對(duì)象悍引,在其構(gòu)造函數(shù)中我們可以指定目錄和緩存大小:

public Cache(File directory, long maxSize);

而如果我們對(duì) OkHttp 內(nèi)置的 Cache 類(lèi)不滿意帽氓,我們可以自行實(shí)現(xiàn) InternalCache 內(nèi)置緩存接口趣斤,在構(gòu)造OkHttpClient 時(shí)進(jìn)行設(shè)置,這樣就可以使用我們自定義的緩存策略了黎休。

建立連接 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");
    HttpStream httpStream = streamAllocation.newStream(client, doExtensiveHealthChecks);
    RealConnection connection = streamAllocation.connection();

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

可以看到浓领,在ConnectInterceptor獲取到StreamAllocation,而StreamAllocation的創(chuàng)建則是在 RetryAndFollowUpInterceptor重定向攔截器這里势腮。

接著調(diào)用到了streamAllocation.newStream联贩,前面介紹到,在newStream方法中會(huì)通過(guò)RealConnection建立與服務(wù)器之間的連接

實(shí)際上建立連接就是創(chuàng)建了一個(gè) HttpCodec 對(duì)象捎拯,它將在后面的步驟中被使用泪幌,那它又是何方神圣呢?它是對(duì) HTTP 協(xié)議操作的抽象署照,有兩個(gè)實(shí)現(xiàn):Http1Codec 和 Http2Codec祸泪,顧名思義,它們分別對(duì)應(yīng) HTTP/1.1 和 HTTP/2 版本的實(shí)現(xiàn)建芙。

在 Http1Codec 中没隘,它利用 Okio 對(duì) Socket 的讀寫(xiě)操作進(jìn)行封裝,Okio 以后有機(jī)會(huì)再進(jìn)行分析禁荸,現(xiàn)在讓我們對(duì)它們保持一個(gè)簡(jiǎn)單地認(rèn)識(shí):它對(duì) java.io 和 java.nio 進(jìn)行了封裝右蒲,讓我們更便捷高效的進(jìn)行 IO 操作。

發(fā)送和接收數(shù)據(jù) CallServerInterceptor

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

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

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

    httpStream.finishRequest();

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

    if (!forWebSocket || response.code() != 101) {
      response = response.newBuilder()
          .body(httpStream.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. 向服務(wù)器發(fā)送 request header赶熟;
  2. 如果有 request body瑰妄,就向服務(wù)器發(fā)送;
  3. 讀取 response header钧大,先構(gòu)造一個(gè) Response 對(duì)象翰撑;
  4. 如果有 response body,就在 3 的基礎(chǔ)上加上 body 構(gòu)造一個(gè)新的 Response 對(duì)象;

CallServerInterceptor首先將http請(qǐng)求頭部發(fā)給服務(wù)器眶诈,如果http請(qǐng)求有body的話涨醋,會(huì)再將body發(fā)送給服務(wù)器,繼而通過(guò)httpStream.finishRequest()結(jié)束http請(qǐng)求的發(fā)送逝撬。

請(qǐng)求完成之后浴骂,我們就可以從 Response 對(duì)象中獲取到響應(yīng)數(shù)據(jù)了,包括 HTTP status code宪潮,status message溯警,response header,response body 等狡相。這里 body 部分最為特殊梯轻,因?yàn)榉?wù)器返回的數(shù)據(jù)可能非常大,所以必須通過(guò)數(shù)據(jù)流的方式來(lái)進(jìn)行訪問(wèn)(當(dāng)然也提供了諸如 string() 和 bytes() 這樣的方法將流內(nèi)的數(shù)據(jù)一次性讀取完畢)尽棕,而響應(yīng)中其他部分則可以隨意獲取喳挑。

響應(yīng) body 被封裝到 ResponseBody 類(lèi)中,該類(lèi)主要有兩點(diǎn)需要注意:

  1. 每個(gè) body 只能被消費(fèi)一次滔悉,多次消費(fèi)會(huì)拋出異常伊诵;
  2. body 必須被關(guān)閉,否則會(huì)發(fā)生資源泄漏回官;

小結(jié)


  • RetryAndFollowUpInterceptor : 創(chuàng)建StreamAllocation對(duì)象曹宴,處理http的重定向,出錯(cuò)重試歉提。對(duì)后續(xù)Interceptor的執(zhí)行的影響:修改request及StreamAllocation笛坦。

  • BridgeInterceptor:補(bǔ)全缺失的一些http header,Cookie設(shè)置唯袄。對(duì)后續(xù)Interceptor的執(zhí)行的影響:修改request弯屈。

  • CacheInterceptor:處理http緩存。對(duì)后續(xù)Interceptor的執(zhí)行的影響:若緩存中有所需請(qǐng)求的響應(yīng)恋拷,則后續(xù)Interceptor不再執(zhí)行资厉。

  • ConnectInterceptor:借助于前面分配的StreamAllocation對(duì)象建立與服務(wù)器之間的連接(具體建立是在newStream方法中),并選定交互所用的協(xié)議是HTTP 1.1還是HTTP 2蔬顾。對(duì)后續(xù)Interceptor的執(zhí)行的影響:創(chuàng)建了httpStream和connection宴偿。

  • CallServerInterceptor:處理IO,與服務(wù)器進(jìn)行數(shù)據(jù)交換诀豁。對(duì)后續(xù)Interceptor的執(zhí)行的影響:為Interceptor鏈中的最后一個(gè)Interceptor窄刘,沒(méi)有后續(xù)Interceptor。

在文章最后我們?cè)賮?lái)回顧一下完整的流程圖:


okhttp_full_process
okhttp_full_process

參考資料

OkHttp3 HTTP請(qǐng)求執(zhí)行流程分析

OKHttp源碼解讀
拆輪子系列:拆 OkHttp

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末舷胜,一起剝皮案震驚了整個(gè)濱河市娩践,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖翻伺,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件材泄,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡吨岭,警方通過(guò)查閱死者的電腦和手機(jī)拉宗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)辣辫,“玉大人旦事,你說(shuō)我怎么就攤上這事〖泵穑” “怎么了姐浮?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)化戳。 經(jīng)常有香客問(wèn)我单料,道長(zhǎng),這世上最難降的妖魔是什么点楼? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮白对,結(jié)果婚禮上掠廓,老公的妹妹穿的比我還像新娘。我一直安慰自己甩恼,他們只是感情好蟀瞧,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著条摸,像睡著了一般悦污。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上钉蒲,一...
    開(kāi)封第一講書(shū)人閱讀 49,007評(píng)論 1 284
  • 那天切端,我揣著相機(jī)與錄音,去河邊找鬼顷啼。 笑死踏枣,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的钙蒙。 我是一名探鬼主播茵瀑,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼躬厌!你這毒婦竟也來(lái)了马昨?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎鸿捧,沒(méi)想到半個(gè)月后屹篓,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡笛谦,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年抱虐,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片饥脑。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡恳邀,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出灶轰,到底是詐尸還是另有隱情谣沸,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布笋颤,位于F島的核電站乳附,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏伴澄。R本人自食惡果不足惜赋除,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望非凌。 院中可真熱鬧举农,春花似錦、人聲如沸敞嗡。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)喉悴。三九已至棱貌,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間箕肃,已是汗流浹背婚脱。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留突雪,地道東北人起惕。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像咏删,于是被迫代替她去往敵國(guó)和親惹想。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

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

  • OkHttp源碼的samples的簡(jiǎn)單使用的示例: public static void main(String....
    _warren閱讀 724評(píng)論 0 1
  • 關(guān)于okhttp是一款優(yōu)秀的網(wǎng)絡(luò)請(qǐng)求框架督函,關(guān)于它的源碼分析文章有很多嘀粱,這里分享我在學(xué)習(xí)過(guò)程中讀到的感覺(jué)比較好的文章...
    蕉下孤客閱讀 3,596評(píng)論 2 38
  • 這段時(shí)間老李的新公司要更換網(wǎng)絡(luò)層激挪,知道現(xiàn)在主流網(wǎng)絡(luò)層的模式是RxJava+Retrofit+OKHttp,所以老李...
    隔壁老李頭閱讀 32,701評(píng)論 51 405
  • 簡(jiǎn)介 目前在HTTP協(xié)議請(qǐng)求庫(kù)中,OKHttp應(yīng)當(dāng)是非撤孢叮火的垄分,使用也非常的簡(jiǎn)單。網(wǎng)上有很多文章寫(xiě)了關(guān)于OkHttp...
    第八區(qū)閱讀 1,371評(píng)論 1 5
  • 這篇文章主要講 Android 網(wǎng)絡(luò)請(qǐng)求時(shí)所使用到的各個(gè)請(qǐng)求庫(kù)的關(guān)系娃磺,以及 OkHttp3 的介紹薄湿。(如理解有誤,...
    小莊bb閱讀 1,145評(píng)論 0 4