OkHttp源碼分析

OkHttp源碼分析

在現(xiàn)在的Android開發(fā)中身弊,請(qǐng)求網(wǎng)絡(luò)獲取數(shù)據(jù)基本上成了我們的標(biāo)配。在早期的Android開發(fā)中會(huì)有人使用HttpClient弥鹦、HttpUrlConnection或者Volley等網(wǎng)絡(luò)請(qǐng)求方式向抢,但對(duì)于如今(2018年)而言,絕大多數(shù)的開發(fā)者都會(huì)使用OkHttp+Retrofit+RxJava進(jìn)行網(wǎng)絡(luò)請(qǐng)求硕淑,而對(duì)于這三者而言课竣,實(shí)際請(qǐng)求網(wǎng)絡(luò)的框架是OkHttp,所以O(shè)kHttp的重要性不言而喻置媳。

OkHttp的基本用法

//創(chuàng)建OkHttpClient對(duì)象
OkHttpClient client = new OkHttpClient();

String run(String url) throws IOException {
   //創(chuàng)建Request請(qǐng)求對(duì)象
  Request request = new Request.Builder()
      .url(url)
      .build();

   //創(chuàng)建Call對(duì)象于樟,并執(zhí)行同步獲取網(wǎng)絡(luò)數(shù)據(jù)
  Response response = client.newCall(request).execute();
  return response.body().string();
}

使用OkHttp基本是以下四步:

  • 創(chuàng)建OkHttpClient對(duì)象
  • 創(chuàng)建Request請(qǐng)求對(duì)象
  • 創(chuàng)建Call對(duì)象
  • 同步請(qǐng)求調(diào)用call.execute();異步請(qǐng)求調(diào)用call.enqueue(callback)
    接下來我會(huì)對(duì)這四步進(jìn)行詳細(xì)的說明拇囊。

創(chuàng)建OkHttpClient對(duì)象

通常來說迂曲,我們使用OkHttp并不會(huì)直接通過new OkHttpClient()來創(chuàng)建出一個(gè)OkHttpClient。一般來說寂拆,我們會(huì)對(duì)這個(gè)OkHttpClient做一些配置奢米,比如:

OkHttpClient.Builder().connectTimeout(
                DEFAULT_MILLISECONDS, TimeUnit.SECONDS).readTimeout(
                DEFAULT_MILLISECONDS, TimeUnit.SECONDS).addInterceptor { chain ->
            val builder = chain.request().newBuilder()
            headerMap?.forEach {
                builder.addHeader(it.key, it.value)
            }
            val request = builder.build()
            chain.proceed(request)
        }.addInterceptor(httpLoggingInterceptor).build()

上面是一段使用Kotlin代碼創(chuàng)建OkHttpClient的過程抓韩,很明顯,OkHttpClient內(nèi)部是使用了 Builder 模式鬓长,好處很明顯: 我們?cè)趧?chuàng)建對(duì)象的同時(shí)可以自由的配置我們需要的參數(shù) 谒拴。我們簡(jiǎn)單看一下OkHttpClient內(nèi)部類Builder中的構(gòu)造方法,看一下OkHttpClient內(nèi)部都可以做哪些配置:

public Builder() {
       //默認(rèn)的分發(fā)器
      dispatcher = new Dispatcher();
      protocols = DEFAULT_PROTOCOLS;
      connectionSpecs = DEFAULT_CONNECTION_SPECS;
      //事件監(jiān)聽工廠
      eventListenerFactory = EventListener.factory(EventListener.NONE);
      proxySelector = ProxySelector.getDefault();
      cookieJar = CookieJar.NO_COOKIES;
      socketFactory = SocketFactory.getDefault();
      hostnameVerifier = OkHostnameVerifier.INSTANCE;
      certificatePinner = CertificatePinner.DEFAULT;
      proxyAuthenticator = Authenticator.NONE;
      authenticator = Authenticator.NONE;
      connectionPool = new ConnectionPool();
      dns = Dns.SYSTEM;
      followSslRedirects = true;
      followRedirects = true;
      retryOnConnectionFailure = true;
      //默認(rèn)連接超時(shí)10s
      connectTimeout = 10_000;
      //默認(rèn)讀取超時(shí)10s
      readTimeout = 10_000;
      //默認(rèn)寫入超時(shí)10s
      writeTimeout = 10_000;
      pingInterval = 0;
    }

上面的代碼中我們非常熟悉的就是連接超時(shí)涉波、讀取超時(shí)英上、寫入超時(shí),它們的默認(rèn)事件都是10s啤覆,其實(shí)這也提醒我們苍日,如果我們想要設(shè)置的超時(shí)時(shí)間也是10s的話,完全沒有必要重復(fù)進(jìn)行配置窗声,其實(shí)我的建議也是不需要配置相恃,直接使用默認(rèn)的就好。值得注意的是 Dispatcher 這個(gè)類笨觅,這是一個(gè)網(wǎng)絡(luò)請(qǐng)求的分發(fā)器拦耐,主要作用是在同步,異步網(wǎng)絡(luò)請(qǐng)求時(shí)會(huì)做一些不同的分發(fā)處理见剩,我們先有個(gè)印象即可杀糯, Dispatcher 會(huì)在之后詳細(xì)的分析。

可能細(xì)心的小伙伴這時(shí)候會(huì)說了:我平時(shí)會(huì)對(duì)OkHttpClient加上一些interceptor來攔截網(wǎng)絡(luò)請(qǐng)求苍苞,比方說在請(qǐng)求之前加上token等請(qǐng)求頭之類的固翰,上面這段代碼為什么沒有攔截器相關(guān)的變量呢?

沒錯(cuò)羹呵,OkHttpClient中的Builder類內(nèi)部確實(shí)是有攔截器相關(guān)成員變量骂际,只不過沒寫在Builder的構(gòu)造方法內(nèi):

public static final class Builder {
    //省略無關(guān)代碼......
    final List<Interceptor> interceptors = new ArrayList<>();
    final List<Interceptor> networkInterceptors = new ArrayList<>();
    //省略無關(guān)代碼......
}

我們平常添加的interceptor就存放在interceptors這個(gè)ArrayList中。OkHttpClient對(duì)象的配置創(chuàng)建不是什么難以理解的點(diǎn)担巩,接下來我們看Request對(duì)象的創(chuàng)建方援。

創(chuàng)建Request請(qǐng)求對(duì)象

為什么要?jiǎng)?chuàng)建Request對(duì)象,很簡(jiǎn)單涛癌,我們請(qǐng)求網(wǎng)絡(luò)需要一些必要的參數(shù)犯戏,比如url,請(qǐng)求方式是get或者post等等信息拳话。而Request這個(gè)類就是對(duì)這些網(wǎng)絡(luò)請(qǐng)求參數(shù)的統(tǒng)一封裝先匪。看一下代碼就一目了然了:

public final class Request {
  final HttpUrl url;
  final String method;
  final Headers headers;
  final @Nullable RequestBody body;
  final Object tag;

  private volatile CacheControl cacheControl; // Lazily initialized.
  //省略無關(guān)代碼......
}

相信大家都能看明白弃衍,這個(gè)Request類中封裝了url呀非、請(qǐng)求方式、請(qǐng)求頭、請(qǐng)求體等等網(wǎng)絡(luò)請(qǐng)求相關(guān)的信息岸裙。Request里面也是一個(gè)Builder模式猖败,這里就不贅述了。

創(chuàng)建Call對(duì)象

Call對(duì)象我們可以這樣理解:Call對(duì)象是對(duì) 一次 網(wǎng)絡(luò)請(qǐng)求的封裝降允。注意這個(gè)關(guān)鍵字: 一次 恩闻,熟悉OkHttp的同學(xué)應(yīng)該都知道,一個(gè)Call對(duì)象只能被執(zhí)行一次剧董,不論是同步execute還是異步的enqueue幢尚,那么這個(gè)只能執(zhí)行一次的特性是如何保證的呢?我們來看代碼:

@Override public Call newCall(Request request) {
    //實(shí)際上是通過 RealCall.newRealCall 來獲取Call對(duì)象
    return RealCall.newRealCall(this, request, false /* for web socket */);
  }

上面的代碼能看到OkHttpClient的newCall實(shí)際上是通過RealCall.newRealCall(this, request, false /* for web socket */)來獲得的翅楼,我們來看一下這個(gè)RealCall:

final class RealCall implements Call {
  final OkHttpClient client;
  //錯(cuò)誤重試與重定向攔截器
  final RetryAndFollowUpInterceptor retryAndFollowUpInterceptor;
  //監(jiān)聽OkHttp網(wǎng)絡(luò)請(qǐng)求各個(gè)階段的事件監(jiān)聽器
  private EventListener eventListener;
  final Request originalRequest;
  final boolean forWebSocket;
  //判斷Call對(duì)象是否被執(zhí)行過的標(biāo)志變量
  private boolean executed;

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

  static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    // Call只是一個(gè)接口尉剩,我們實(shí)際創(chuàng)建的是Call的實(shí)現(xiàn)類RealCall的對(duì)象
    RealCall call = new RealCall(client, originalRequest, forWebSocket);
    call.eventListener = client.eventListenerFactory().create(call);
    return call;
  }
  
  Override public Response execute() throws IOException {
    //確保線程安全的情況下通過executed來保證每個(gè)Call只被執(zhí)行一次
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
   //省略無關(guān)代碼......
  }
  
  @Override public void enqueue(Callback responseCallback) {
     /確保線程安全的情況下通過executed來保證每個(gè)Call只被執(zhí)行一次
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    //省略無關(guān)代碼
  }
  //省略無關(guān)代碼......
}

我們可以看到,Call只是一個(gè)接口毅臊,我們創(chuàng)建的實(shí)際上是RealCall對(duì)象理茎。在RealCall中存在一個(gè) execute 的成員變量褂微,在execute()和enqueue(Callback responseCallback) 方法中都是通過 execute 來確保每個(gè)RealCall對(duì)象只會(huì)被執(zhí)行一次功蜓。

創(chuàng)建Call對(duì)象的過程其實(shí)也是很簡(jiǎn)單的,麻煩的地方在最后一步: **execute()和enqueue(Callback responseCallback) **

同步請(qǐng)求與異步請(qǐng)求

前三步非常簡(jiǎn)單宠蚂,我們可以知道并沒有涉及網(wǎng)絡(luò)的請(qǐng)求,所以核心肯定是在這關(guān)鍵的第四步童社。

同步請(qǐng)求execute()和異步請(qǐng)求enqueue(Callback responseCallback)

先說同步請(qǐng)求求厕,看代碼:

@Override public Response execute() throws IOException {
    //通過executed確保每個(gè)Call對(duì)象只會(huì)被執(zhí)行一次
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    //網(wǎng)絡(luò)請(qǐng)求開始的回調(diào)
    eventListener.callStart(this);
    try {
      //調(diào)用分發(fā)器的executed(this)方法
      client.dispatcher().executed(this);
      //真實(shí)的網(wǎng)絡(luò)請(qǐng)求是在這里處理的
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } catch (IOException e) {
      //網(wǎng)絡(luò)請(qǐng)求失敗的回調(diào)
      eventListener.callFailed(this, e);
      throw e;
    } finally {
      //網(wǎng)絡(luò)請(qǐng)求結(jié)束
      client.dispatcher().finished(this);
    }
  }

execute() 方法中首先通過executed確保每個(gè)Call對(duì)象只會(huì)被執(zhí)行一次,之后調(diào)用了eventListener.callStart(this);來執(zhí)行網(wǎng)絡(luò)請(qǐng)求開始的回調(diào)扰楼。接下來調(diào)用了client.dispatcher().executed(this)呀癣,那么這句代碼具體是做了什么呢:

public Dispatcher dispatcher() {
    //返回了一個(gè)OkHttpClient內(nèi)部的dispather分發(fā)器
    return dispatcher;
  }

這句代碼首先返回一個(gè) dispatcher ,這個(gè)分發(fā)器我們?cè)谏厦嬉蔡岬竭^弦赖,這是一個(gè)比較重要的概念项栏,來看一下這個(gè)分發(fā)器:

public final class Dispatcher {
  //最大請(qǐng)求數(shù)
  private int maxRequests = 64;
  //每個(gè)host的最大請(qǐng)求數(shù)
  private int maxRequestsPerHost = 5;
  //網(wǎng)絡(luò)請(qǐng)求處于空閑時(shí)的回調(diào)
  private @Nullable Runnable idleCallback;
  //線程池的實(shí)現(xiàn)
  private @Nullable ExecutorService executorService;
  //就緒等待網(wǎng)絡(luò)請(qǐng)求的異步隊(duì)列
  private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
  //正在執(zhí)行網(wǎng)絡(luò)請(qǐng)求的異步隊(duì)列
  private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
  //正在執(zhí)行網(wǎng)絡(luò)請(qǐng)求的同步隊(duì)列
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
  
  //忽略無關(guān)代碼......
  
  synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);
      executorService().execute(call);
    } else {
      readyAsyncCalls.add(call);
    }
  }
  
  synchronized void executed(RealCall call) {
    //將call對(duì)象加入網(wǎng)絡(luò)請(qǐng)求的同步隊(duì)列中
    runningSyncCalls.add(call);
  }
  
  //忽略無關(guān)代碼......
}

可以看到 Dispatcher 這個(gè)分發(fā)器類內(nèi)部定義了很多的成員變量:maxRequests 最大請(qǐng)求個(gè)數(shù),默認(rèn)值是64蹬竖; maxRequestsPerHost 每個(gè)host的最大請(qǐng)求個(gè)數(shù)沼沈,這個(gè)host是什么?舉個(gè)栗子币厕,一個(gè)URL為 http://gank.io/api 列另,那么host就是 http://gank.io/ 相當(dāng)于baseUrl。 ** idleCallback** 這是一個(gè)空閑狀態(tài)時(shí)的回調(diào)旦装,當(dāng)我們的所有的網(wǎng)絡(luò)請(qǐng)求隊(duì)列為空時(shí)會(huì)執(zhí)行页衙。 executorService 這是一個(gè)線程池,主要是為了高效執(zhí)行異步的網(wǎng)絡(luò)請(qǐng)求而創(chuàng)建的線程池,之后會(huì)再次提到它店乐。接下來就是比較重要的三個(gè)隊(duì)列:

  • readyAsyncCalls -> 在就緒等待的異步Call隊(duì)列
  • runningAsyncCalls -> 正在執(zhí)行的異步Call隊(duì)列
  • runningSyncCalls -> 正在執(zhí)行的同步Call隊(duì)列

對(duì)于這三個(gè)隊(duì)列來說艰躺,執(zhí)行同步請(qǐng)求的Call對(duì)象會(huì)加入到runningSyncCalls中;執(zhí)行異步請(qǐng)求的Call對(duì)象會(huì)加入到readyAsyncCalls或者runningAsyncCalls中眨八,那么什么時(shí)候加入到等待隊(duì)列描滔,什么時(shí)候加入到執(zhí)行隊(duì)列呢?簡(jiǎn)單的說踪古,如果執(zhí)行異步網(wǎng)絡(luò)請(qǐng)求的線程池很忙含长,異步請(qǐng)求的Call對(duì)象會(huì)加入到等待隊(duì)列;反之則加入到執(zhí)行隊(duì)列伏穆。那么這個(gè)忙于不忙的標(biāo)準(zhǔn)是什么呢拘泞?很簡(jiǎn)單,在enqueue方法中有runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost一個(gè)判斷的標(biāo)準(zhǔn)枕扫,即正在執(zhí)行的異步隊(duì)列中Call對(duì)象個(gè)數(shù)小于maxRequests(64)并且執(zhí)行隊(duì)列中的同一個(gè)host對(duì)應(yīng)的Call對(duì)象個(gè)數(shù)小于maxRequestsPerHost(5)的時(shí)候陪腌。

說完了 Dispatcher 關(guān)鍵的成員變量,我們來看一下它的 **executed(RealCall call) ** 方法:

 synchronized void executed(RealCall call) {
    //將call對(duì)象加入網(wǎng)絡(luò)請(qǐng)求的同步隊(duì)列中
    runningSyncCalls.add(call);
  }

這是一個(gè)synchronized修飾的方法烟瞧,為了確保線程安全诗鸭。Dispatcher中的executed(RealCall call)方法及其簡(jiǎn)單,就是把Call對(duì)象加入到同步Call隊(duì)列中参滴。對(duì)强岸,你沒有看錯(cuò),它確實(shí)就只有這一行代碼砾赔,沒什么復(fù)雜的操作蝌箍。

說完了 Dispatcher 中的同步方法,我們?cè)賮砜匆幌庐惒剑?/p>

  synchronized void enqueue(AsyncCall call) {
      //判斷Call對(duì)象應(yīng)該添加到等待隊(duì)列還是執(zhí)行隊(duì)列
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      //加入執(zhí)行隊(duì)列
      runningAsyncCalls.add(call);
      //線程池開啟線程執(zhí)行異步網(wǎng)絡(luò)請(qǐng)求
      executorService().execute(call);
    } else {
      //加入等待隊(duì)列
      readyAsyncCalls.add(call);
    }
  }

和同步方法相比暴心,異步方法中的內(nèi)容要稍微多一點(diǎn)妓盲。首先是判斷Call對(duì)象應(yīng)該添加到等待隊(duì)列還是執(zhí)行隊(duì)列,這個(gè)判斷上面已經(jīng)說過专普。加入執(zhí)行隊(duì)列后悯衬,開啟線程池并執(zhí)行Call對(duì)象。這里需要注意的是異步請(qǐng)求時(shí)的Call對(duì)象和同步請(qǐng)求時(shí)不一樣檀夹,會(huì)轉(zhuǎn)換成一個(gè) AsyncCall 對(duì)象筋粗,這個(gè) AsyncCall 實(shí)際上是一個(gè) NamedRunnable ,那既然是一個(gè) Runnable 击胜,我們肯定要看一下它的execute()方法:

@Override protected void execute() {
      boolean signalledCallback = false;
      try {
        //核心的請(qǐng)求網(wǎng)絡(luò)方法
        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 {
          eventListener.callFailed(RealCall.this, e);
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }
  }

其實(shí)整段代碼看似非常多亏狰,核心就只有Response response = getResponseWithInterceptorChain()這一句:通過攔截器鏈獲取網(wǎng)絡(luò)返回結(jié)果。其實(shí)不止是異步請(qǐng)求偶摔,同步請(qǐng)求的核心也是這一行代碼暇唾。我們繼續(xù)看一下RealCall中的execute方法:

@Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    try {
      client.dispatcher().executed(this);
      //跟異步請(qǐng)求一樣,核心也是通過攔截器鏈來獲取網(wǎng)絡(luò)數(shù)據(jù)
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } catch (IOException e) {
      eventListener.callFailed(this, e);
      throw e;
    } finally {
      client.dispatcher().finished(this);
    }
  }

很明顯,在client.dispatcher().executed(this)將Call對(duì)象加入同步請(qǐng)求隊(duì)列中之后策州,同樣調(diào)用的是Response result = getResponseWithInterceptorChain()瘸味。明白了嗎,不論是在同步請(qǐng)求或者是異步請(qǐng)求够挂,最終獲取網(wǎng)絡(luò)數(shù)據(jù)的核心處理都是一致的:getResponseWithInterceptorChain() 旁仿。

接下來我們來分析這個(gè)在OkHttp中非常核心的方法:

Response getResponseWithInterceptorChain() throws IOException {
    //創(chuàng)建存放攔截器的list
    List<Interceptor> interceptors = new ArrayList<>();
    //攔截器列表加入我們配置OkHttpClient時(shí)添加的攔截器
    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) {
      //如果不是針對(duì)WebSocket的網(wǎng)絡(luò)訪問,加入網(wǎng)絡(luò)攔截器
      interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(forWebSocket));
    //創(chuàng)建攔截器鏈
    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
        originalRequest, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    //執(zhí)行攔截器鏈
    return chain.proceed(originalRequest);
  }

在這個(gè)方法中首先創(chuàng)建了一個(gè)ArrayList孽糖,用來存放所有的攔截器枯冈。從上到下可以看到,一共是添加了7中不同的攔截器:

  • client.interceptors() -> 我們自己添加的請(qǐng)求攔截器办悟,通常是做一些添加統(tǒng)一的token之類操作
  • retryAndFollowUpInterceptor -> 主要負(fù)責(zé)錯(cuò)誤重試和請(qǐng)求重定向
  • BridgeInterceptor -> 負(fù)責(zé)添加網(wǎng)絡(luò)請(qǐng)求相關(guān)的必要的一些請(qǐng)求頭尘奏,比如Content-Type、Content-Length病蛉、Transfer-Encoding炫加、User-Agent等等
  • CacheInterceptor -> 負(fù)責(zé)處理緩存相關(guān)操作
  • ConnectInterceptor -> 負(fù)責(zé)與服務(wù)器進(jìn)行連接的操作
  • networkInterceptors -> 同樣是我們可以添加的攔截器的一種,它與client.interceptors() 不同的是二者攔截的位置不一樣铺然。
  • CallServerInterceptor -> 在這個(gè)攔截器中才會(huì)進(jìn)行真實(shí)的網(wǎng)絡(luò)請(qǐng)求

在添加完各種攔截器后俗孝,創(chuàng)建了一個(gè)攔截器鏈,然后執(zhí)行了攔截器鏈的proceed方法魄健,我們來看一下這個(gè)proceed方法:

@Override public Response proceed(Request request) throws IOException {
    return proceed(request, streamAllocation, httpCodec, connection);
  }

這個(gè)方法調(diào)用的是 RealInterceptorChain 內(nèi)部的另一個(gè)proceed方法赋铝,再跟進(jìn)去看一下:

public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
     RealConnection connection) throws IOException {
   //忽略無關(guān)代碼......
   
   // 獲取攔截鏈中的下一個(gè)攔截器
   RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
       connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
       writeTimeout);
   Interceptor interceptor = interceptors.get(index);
   //通過調(diào)用攔截器的intercept方法獲取網(wǎng)絡(luò)數(shù)據(jù)
   Response response = interceptor.intercept(next);
   
   //忽略無關(guān)代碼......
   
   return response;
 }

這個(gè)方法中主要分為兩步:獲取攔截鏈中的下一個(gè)攔截器,然后調(diào)用這個(gè)攔截器的 intercept(next) 方法诀艰,在構(gòu)建OkHttpClietn時(shí)添加過interceptor的同學(xué)應(yīng)該都比較清楚柬甥,在intercept()方法中我們必須要存在chain.proceed(request)這樣一句代碼。類似于這樣:

OkHttpClient.Builder().connectTimeout(
                DEFAULT_MILLISECONDS, TimeUnit.SECONDS).readTimeout(
                DEFAULT_MILLISECONDS, TimeUnit.SECONDS).addInterceptor { chain ->
            val builder = chain.request().newBuilder()
            headerMap?.forEach {
                builder.addHeader(it.key, it.value)
            }
            val request = builder.build()
            //將攔截器鏈執(zhí)行下去
            chain.proceed(request)
        }.addInterceptor(httpLoggingInterceptor).build()

每個(gè)攔截器內(nèi)部的intercept方法內(nèi)部必須存在chain.proceed(request)其垄,同樣,OkHttp提供的攔截器的intercept方法內(nèi)部都必須存在chain.proceed(request)這句代碼卤橄,除了最后一個(gè)攔截器CallServerInterceptor绿满。

整個(gè)邏輯是不是有些混亂,沒關(guān)系窟扑,我們來整理一下喇颁。

  • getResponseWithInterceptorChain()方法通過調(diào)用chain.proceed(originalRequest)開啟攔截鏈。
  • 在RealInterceptorChain的proceed(request)方法中會(huì)調(diào)用下一個(gè)攔截器中的intercept(chain)方法
  • 在攔截器中的intercept(chain)中會(huì)調(diào)用chain.proceed(request)
    上面是一個(gè)循環(huán)調(diào)用嚎货,由于每次獲取的攔截器是攔截器列表中的下一個(gè)攔截器橘霎,所以實(shí)現(xiàn)了順序調(diào)用攔截器列表中的每個(gè)不同的攔截器的攔截方法。因?yàn)樽詈笠粋€(gè)攔截器并沒有調(diào)用chain.proceed(request)殖属,所以能夠結(jié)束循環(huán)調(diào)用姐叁。

再來張圖,加深大家對(duì)攔截器鏈的理解:


OkHttp攔截器鏈.jpg

看完這張圖,小伙伴們應(yīng)該會(huì)對(duì)整個(gè)攔截器鏈的運(yùn)作流程有一定的了解外潜。到此為止原环,OkHttp的源碼分析就告一段落了,具體每個(gè)攔截器中的實(shí)現(xiàn)細(xì)節(jié)处窥,大家如果有興趣的話可以自己去深入了解一下嘱吗,我這里就不再贅述了。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末滔驾,一起剝皮案震驚了整個(gè)濱河市谒麦,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌哆致,老刑警劉巖绕德,帶你破解...
    沈念sama閱讀 219,490評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異沽瞭,居然都是意外死亡迁匠,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門驹溃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來城丧,“玉大人,你說我怎么就攤上這事豌鹤⊥龊澹” “怎么了?”我有些...
    開封第一講書人閱讀 165,830評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵布疙,是天一觀的道長(zhǎng)蚊惯。 經(jīng)常有香客問我,道長(zhǎng)灵临,這世上最難降的妖魔是什么截型? 我笑而不...
    開封第一講書人閱讀 58,957評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮儒溉,結(jié)果婚禮上宦焦,老公的妹妹穿的比我還像新娘。我一直安慰自己顿涣,他們只是感情好波闹,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評(píng)論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著涛碑,像睡著了一般精堕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蒲障,一...
    開封第一講書人閱讀 51,754評(píng)論 1 307
  • 那天歹篓,我揣著相機(jī)與錄音瘫证,去河邊找鬼。 笑死滋捶,一個(gè)胖子當(dāng)著我的面吹牛痛悯,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播重窟,決...
    沈念sama閱讀 40,464評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼载萌,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了巡扇?” 一聲冷哼從身側(cè)響起扭仁,我...
    開封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎厅翔,沒想到半個(gè)月后乖坠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,847評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡刀闷,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評(píng)論 3 338
  • 正文 我和宋清朗相戀三年熊泵,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片甸昏。...
    茶點(diǎn)故事閱讀 40,137評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡顽分,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出施蜜,到底是詐尸還是另有隱情卒蘸,我是刑警寧澤,帶...
    沈念sama閱讀 35,819評(píng)論 5 346
  • 正文 年R本政府宣布翻默,位于F島的核電站缸沃,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏修械。R本人自食惡果不足惜趾牧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望肯污。 院中可真熱鬧武氓,春花似錦、人聲如沸仇箱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)剂桥。三九已至,卻和暖如春属提,著一層夾襖步出監(jiān)牢的瞬間权逗,已是汗流浹背美尸。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留斟薇,地道東北人师坎。 一個(gè)月前我還...
    沈念sama閱讀 48,409評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像堪滨,于是被迫代替她去往敵國(guó)和親胯陋。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評(píng)論 2 355

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