Android技能樹 — 網絡小結(6)之 OkHttp超超超超超超超詳細解析

前言:

本文也作了一次標題黨,哈哈,其實寫的仍是很水,各位原諒我O(∩_∩)O。html

介於本身的網絡方面知識爛的一塌糊塗,因此準備寫相關網絡的文章,可是考慮所有寫在一篇太長了,因此分開寫,但願你們能仔細看,最好能夠指出個人錯誤,讓我也能糾正。java

1.講解相關的整個網絡體系結構:web

Android技能樹 — 網絡小結(1)之網絡體系結構面試

2.講解相關網絡的重要知識點,好比不少人都聽過相關網絡方面的名詞,可是僅限於聽過而已,什麼tcp ,udp ,socket ,websocket, http ,https ,而後webservice是啥,跟websocket很像,socket和websocket啥關係長的也很像,session,token,cookie又是啥。api

Android技能樹 — 網絡小結(2)之TCP/UDP緩存

Android技能樹 — 網絡小結(3)之HTTP/HTTPSbash

Android技能樹 — 網絡小結(4)之socket/websocket/webservice服務器

相關網絡知識點小結- cookie/session/token(待寫)websocket

3.相關的第三方框架的源碼解析,畢竟如今面試個大點的公司,okhttp和retrofit源碼是必問的。cookie

Android技能樹 — 網絡小結(6)之 OkHttp超超超超超超超詳細解析

Android技能樹 — 網絡小結(7)之 Retrofit源碼詳細解析


這裏提一個本文無關的小知識點,不少文章開頭都會提到,咱們以okhttp3.xxx版原本講解,那怎麼看當前最新的已是幾了呢?(主要之前也有人問過我在哪裏查看xxx第三方庫最新的版本,因此想到提一下這個)其實很簡單,咱們以okhttp爲例:

  1. Android Studio直接查看:
  2. JCenter上查看: JCenter上搜索Okhttp版本
  3. Maven上查看: Maven上搜索Okhttp版本
  4. ........其餘方式

正文

看不清楚的,能夠右鍵,選擇新標籤頁中打開,而後點擊圖片放大

首先咱們來肯定整體大綱:

  1. okhttp相關參數配置,好比設置超時時間,網絡路徑等等等等等.......
  2. 咱們知道在使用okhttp的時候可使用同步請求,也可使用異步請求,因此確定不一樣的請求,在分發的時候有不一樣的處理。
  3. 咱們之前網絡系列的文章提過,發送到後臺,確定是一個完整的請求包,可是咱們使用okhttp的時候,只是轉入了咱們須要給後臺的參數,甚至咱們若是是get請求,只是傳入了相應的url網絡地址就能拿到數據,說明okhttp幫咱們把簡單的參數輸入,而後經過一系列的添加封裝,而後變成一個完整的網絡請求包出去,而後咱們在使用okhttp的時候,拿到返回的數據也已是咱們能夠直接用的對象,說明接受的時候,已經幫咱們把拿到的返回網絡包,解析成咱們直接用的對象了因此在一系列幫咱們發送的時候添加參數變成完整網絡請求包,收到時候幫咱們解析返回請求包的過程,是Okhttp的一個個攔截器們所處理,它攔截到咱們的數據,而後進行處理,好比添加一些數據,變成完整的網絡請求包等操做

因此咱們大概就知道了okhttp通常的主要內容爲這三大塊。

1.okhttp基礎使用:

講解源碼前,先寫上okhttp基本使用,這樣才更方便講解源碼:

String url = "http://www.baidu.com";
//'1. 生成OkHttpClient實例對象'
OkHttpClient okHttpClient = new OkHttpClient();
//'2. 生成Request對象'
Request request = new Request.Builder().url(url).build();
//'3. 生成Call對象'
Call call = okHttpClient.newCall(request);
//'4. 若是要執行同步請求:'
try {
    call.execute();
} catch (IOException e) {
    e.printStackTrace();
}
//'5. 若是要執行異步請求:'
call.enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
    }

    @Override
    public void onResponse(Call call, Response response) throws IOException {
    }
});
複製代碼

2. 初始化相關參數解析:

咱們來看咱們最剛開始的完整流程圖:

而後配合上面第一步的okhttp基本使用,發如今執行同步和異步前,咱們要先準備好OkhttpClientRequestCall對象。咱們一步步來看相關源碼:

2.1 OkHttpClient相關:

咱們上面的代碼實例化OkHttpClient對象的代碼是:

OkHttpClient okHttpClient = new OkHttpClient();
複製代碼

咱們進入查看:

發現 OkHttpClient除了空參數的構造函數,還有一個傳入 Builder的構造函數,而咱們的 new OkHttpClient()最終也是調用了傳入 Builder的構造函數,只不過傳入默認的 Builder對象值,以下圖所示:

咱們能夠看到最後幾個值:

......
connectTimeout = 10_000;
readTimeout = 10_000;
writeTimeout = 10_000;
......
複製代碼

默認的鏈接超時,讀取超時,寫入超時,都爲10秒,而後還有其餘等默認屬性,那咱們加入想要改變這些屬性值呢,好比超時時間改成20秒,很簡單。咱們不使用默認的Builder對象便可:

OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout(20,TimeUnit.SECONDS);
builder.readTimeout(20,TimeUnit.SECONDS);
builder.writeTimeout(20,TimeUnit.SECONDS);
OkHttpClient okHttpClient = builder.build();

//這裏不能直接使用那個傳入Builder對象的OkHttpClient的構造函數,由於該構造函數的方法不是public的
OkHttpClient okHttpClient = new OkHttpClient(builder);//這樣是錯誤的
builder.build();的源碼是:
public OkHttpClient build() {
    return new OkHttpClient(this);
}
複製代碼

咱們再回過頭來看看OkHttpClient裏面設置的屬性值都有什麼用:

  • Dispatch:分發器,後面會提到

  • Proxy:設置代理,一般爲類型(http、socks)和套接字地址。參考文章:直接使用Proxy建立鏈接

  • ProxySelector: 設置全局的代理,經過繼承該類,設置具體代理的類型、地址和端口。參考文章:Java代理 經過ProxySelector設置全局代理

  • Protocol: 網絡協議類,好比咱們常常聽到的http1.0、http1.一、http2.0協議等。

  • ConnectionSpec: 指定HTTP流量經過的套接字鏈接的配置。咱們直接能夠翻譯該類頭部的英文介紹,具體的內容原諒我也不是很懂:

  • Interceptor:攔截器,後面會提到

  • EventListener:指標事件的監聽器。擴展此類以監視應用程序的HTTP調用的數量,大小和持續時間。全部start/connect/acquire事件最終都會收到匹配的end /release事件,要麼成功(非null參數)要麼失敗(非null throwable)。每一個事件對的第一個公共參數用於在併發或重複事件的狀況下連接事件,例如dnsStart(call,domainName);和dnsEnd(call,domainName,inetAddressList); 咱們能夠看到一系列的xxxStart和xxxEnd方法:

  • CookieJar:向傳出的HTTP請求添加cookie,收到的HTTP返回數據的cookie處理。

    參考文章:okhttp3帶cookie請求

  • Cache:網絡緩存,okhttp默認只能設置緩存GET請求,不緩存POST請求,畢竟POST請求不少都是交互的,緩存下來也沒有什麼意義。

    咱們看到Cache的構造函數,能夠看到的是須要設置緩存文件夾,緩存的大小,還有一個是緩存內部的操做方式,由於緩存是須要寫入文件的,默認操做使用的是Okio來操做。
    參考文章:
    教你如何使用okhttp緩存
    OKHTTP之緩存配置詳解

  • InternalCache:Okhttp內部緩存的接口,咱們直接使用的時候不須要去實現這個接口,而是直接去使用上面的Cache類。

  • SocketFactory:從字面意思就看的出來,Android 自帶的Socket的工廠類。
    參考文章: 類SocketFactory

  • SSLSocketFactory:Android自帶的SSLSocket的工廠類。
    參考文章:Java SSLSocket的使用
    用SSLSocketFactory 鏈接https的地址

  • CertificateChainCleaner:不是很瞭解,因此仍是老樣子,經過谷歌翻譯,翻譯該類的頂部備註說明:

  • HostnameVerifier:字面意思,Host name 驗證,這個一個基礎接口,並且只有一個方法:

/**
 * Verify that the host name is an acceptable match with
 * the server ‘s authentication scheme.
 *
 * @param hostname the host name
 * @param session SSLSession used on the connection to host
 * @return true if the host name is acceptable
 */
public boolean verify(String hostname, SSLSession session);
複製代碼
  • Dns:DNS(Domain Name System,域名系統),dns用於將域名解析解析爲ip地址。
    參考文章:Android DNS更新與DNS-Prefetch
  • 還有其餘等等......

2.2 Request相關

咱們查看Request代碼:

public final class Request {
  final HttpUrl url; //網絡請求路徑
  final String method; //get、post.....
  final Headers headers;//請求頭
  final @Nullable RequestBody body;//請求體
  /**
  你能夠經過tags來同時取消多個請求。
  當你構建一請求時,使用RequestBuilder.tag(tag)來分配一個標籤。
  以後你就能夠用OkHttpClient.cancel(tag)來取消全部帶有這個tag的call。.
  */
  final Map<Class<?>, Object> tags;

  .......
  .......
  .......
  
}
複製代碼

這個估計不少人都清楚,若是對請求頭請求體等不清楚的,能夠看下之前咱們這個系列的文章:Android技能樹 — 網絡小結(3)之HTTP/HTTPS

2.3 Call相關

咱們能夠看到咱們生成的Request實例,會傳給OkHttpClient實例的newÇall方法:

Request request = new Request.Builder().url(url).build();
Call call = okHttpClient.newCall(request);
call.execute();或者 call.enqueue(....);
複製代碼

咱們Request和OkHttpClient大體都瞭解過了,咱們來具體看下newCall執行了什麼和Call的具體內容。

Call類代碼:

@Override public Call newCall(Request request) {
    return RealCall.newRealCall(this, request, false /* for web socket */);
}

RealCall類代碼:
static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    // Safely publish the Call instance to the EventListener.
    RealCall call = new RealCall(client, originalRequest, forWebSocket);
    call.eventListener = client.eventListenerFactory().create(call);
    return call;
}

複製代碼

咱們能夠看到,最後獲取到的是RealCall的實例,同時把咱們各類參數都配置好的OkHttpClient和Request都傳入了。

因此後面call.execute()/call.enqueue()都是執行的RealCall的相對應的方法。但目前位置咱們上面的圖已經講解好了,我這裏再貼一次:

恭喜你,下次別人考你Okhttp前面的相關參數配置方面的代碼你已經都理解了。

3.請求分發Dispatcher

咱們繼續看咱們的流程圖下面的內容:

3.1 Dispatcher 同步操做

咱們先來說同步執行:

@Override 
public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    try {
      //'1. 執行了dispatcher的executed方法'
      client.dispatcher().executed(this);
      //'2. 調用了getResponseWithInterceptorChain方法'
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } catch (IOException e) {
      eventListener.callFailed(this, e);
      throw e;
    } finally {
      //'3. 最後必定會執行dispatcher的finished方法'
      client.dispatcher().finished(this);
    }
}
複製代碼

咱們一步步來具體看,第一步看Dispatcher類中的executed方法了:

/** Used by {@code Call#execute} to signal it is in-flight. */
  synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
}
複製代碼

能夠看到把咱們的RealCall加入到了一個同步線程runningSyncCalls中,而後中間調用了getResponseWithInterceptorChain方法*(這個第二個操做咱們會放在後面很具體的講解),咱們既然加入到了一個同步線程中,確定用完了要移除,而後第三步finished方法會作處理:

/** Used by {@code Call#execute} to signal completion. */
void finished(RealCall call) {
   finished(runningSyncCalls, call, false);
}

private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
    int runningCallsCount;
    Runnable idleCallback;
    synchronized (this) {
    
      //'if語句裏面咱們能夠看到這裏把咱們的隊列中移除了call對象'
      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();
    }
}
複製代碼

3.2 Dispatcher 異步操做

咱們先來看RealCall裏面的enqueue代碼:

@Override public void enqueue(Callback responseCallback) {
    //'1. 這裏有個同步鎖的拋異常操做'
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    //'2. 調用Dispatcher裏面的enqueue方法'
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
複製代碼

咱們一步步來看,第一個同步鎖拋異常的操做,咱們知道一個Call應對一個網絡請求,加入你這麼寫是錯誤的:

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 {}
});
//'同一個call對象再次發起請求'
call.enqueue(new Callback() {
   @Override
   public void onFailure(Call call, IOException e) {}

   @Override
   public void onResponse(Call call, Response response) throws IOException {}
});
複製代碼

同一個Call對象,同時請求了二次。這時候就會進入咱們的同步鎖判斷,只要一個執行過了,裏面 executed會爲true,也就會拋出異常。

咱們再來看第二步操做:

咱們知道異步請求,確定會表明不少請求都在各自的線程中去執行,那麼咱們在不看OkHttp源碼前,讓你去實現,你怎麼實現,是否是第一個反應是使用線程池。

Java/Android線程池框架的結構主要包括3個部分

1.任務:包括被執行任務須要實現的接口類:Runnable 或 Callable

2.任務的執行器:包括任務執行機制的核心接口類Executor,以及繼承自Executor的EexcutorService接口。

3.執行器的建立者,工廠類Executors
複製代碼

具體能夠參考:Android 線程池框架、Executor、ThreadPoolExecutor詳解

client.dispatcher().enqueue(new AsyncCall(responseCallback));,再也不是像同步操做同樣,直接把RealCall傳入,而是傳入一個AsyncCall對象。沒錯,按照咱們上面提到的線程池架構,任務是使用Runnable 或 Callable接口,咱們查看AsyncCall的代碼:

final class AsyncCall extends NamedRunnable {
    ......
    ......
}

public abstract class NamedRunnable implements Runnable {
    .......
    .......
}

複製代碼

果真如咱們預計,是使用了Runnable接口。

client.dispatcher().enqueue(new AsyncCall(responseCallback));,再也不是像同步操做同樣,直接把RealCall傳入,而是傳入一個AsyncCall對象。

調用Dispatcher裏面的enqueue方法:

synchronized void enqueue(AsyncCall call) {
    //'1. 判斷當前異步隊列裏面的數量是否小於最大值,當前請求數是否小於最大值'
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      //'2. 若是沒有大於最大值,則將call加入到異步請求隊列中'
      runningAsyncCalls.add(call);
      //'3. 而且運行call的任務'
      executorService().execute(call);
    } else {
      readyAsyncCalls.add(call);
    }
}
複製代碼

我麼直接看第三步,按照咱們上面提到過的Java/Android線程池框架的結構主要包括3個部分,能夠看到執行咱們的Runnable對象的,說明他是一個任務執行器,也就是Executor的繼承類。說明executorService()返回了一個Executor的實現類,咱們點進去查看:

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

複製代碼

果真建立一個可緩存線程池,線程池的最大長度無限制,但若是線程池長度超過處理須要,可靈活回收空閒線程,若無可回收,則新建線程。

那咱們知道是線程池執行了Runnable的任務,那咱們只要具體看咱們的okhttp的Runnable到底執行了什麼便可:

final class AsyncCall extends NamedRunnable {
    private final Callback responseCallback;

    AsyncCall(Callback responseCallback) {
      super("OkHttp %s", redactedUrl());
      this.responseCallback = responseCallback;
    }
    
    ......
    ......
    ......
    
    @Override protected void execute() {
      boolean signalledCallback = false;
      try {
      
        //'1. 咱們能夠發現最後線程池執行的任務就是getResponseWithInterceptorChain方法'
        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 {
        //'2. 最後再從Dispatcher裏面的異步隊列中移除'
        client.dispatcher().finished(this);
      }
    }
  }
複製代碼

咱們發現無論是異步仍是同步,都是同樣的三部曲:1.加入到Dispatcher裏面的同步(或異步)隊列,2.執行getResponseWithInterceptorChain方法,3.從Dispatcher裏面的同步(或異步)隊列移除。(只不過同步操做是直接運行了getResponseWithInterceptorChain方法,而異步是經過線程池執行Runnable再去執行getResponseWithInterceptorChain方法)

4 Okhttp攔截

咱們在前面已經知道了無論是異步請求仍是同步請求,都會去執行 RealCallgetResponseWithInterceptorChain操做:

Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    //'1. 建立一個攔截器List'
    List<Interceptor> interceptors = new ArrayList<>();
    //'2. 添加用戶本身建立的應用攔截器'
    interceptors.addAll(client.interceptors());
    //'3. 添加劇試與重定向攔截器'
    interceptors.add(retryAndFollowUpInterceptor);
    //'4. 添加內容攔截器'
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    //'4. 添加緩存攔截器'
    interceptors.add(new CacheInterceptor(client.internalCache()));
    /'5. 添加鏈接攔截器'
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
      //'6. 添加用戶本身建立的網絡攔截器'
      interceptors.addAll(client.networkInterceptors());
    }
    //'7. 添加請求服務攔截器'
    interceptors.add(new CallServerInterceptor(forWebSocket));

    //'8.把這些攔截器們一塊兒封裝在一個攔截器鏈條上面(RealInterceptorChain)'
    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
        originalRequest, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());
    
    //'9.而後執行鏈條的proceed方法'
    return chain.proceed(originalRequest);
  }
複製代碼

咱們先無論具體的攔截器的功能,咱們先來看總體的執行方式,因此咱們直接來看攔截器鏈條的工做模式:

public final class RealInterceptorChain implements Interceptor.Chain {
   
   //'咱們剛纔創建的放攔截器的隊列'
   private final List<Interceptor> interceptors;
   //'當前執行的第幾個攔截器序號'
   private final int index;
   ......
   ......
   ......
  

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

   //'實例化了一個新的RealInterceptorChain對象,而且傳入相同的攔截器List,只不過傳入的index值+1'
   RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
       connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
       writeTimeout);
   //'獲取當前index對應的攔截器裏面的具體的某個攔截器, Interceptor interceptor = interceptors.get(index); //而後執行攔截器的intercept方法,同時傳入新的RealInterceptorChain對象(主要的區別在於index+1了)'
   Response response = interceptor.intercept(next);
   
   ......
   ......
   ......

   return response;
 }
}
複製代碼

咱們能夠看到在RealInterceptorChain類的proceed的方法裏面,又去實例化了一個RealInterceptorChain類。不少人可能看着比較繞,不要緊,咱們舉個例子簡單說下就能夠了:

個人寫法仍是按照它的寫法,寫了二個Interceptor,一個用來填充地址AddAddressInterceptor,一箇中來填充電話AddTelephoneInterceptor,而後也創建一個攔截鏈條InterceptorChain。這樣我只須要傳進去一個字符串,而後會自動按照每一個攔截器的功能,自動幫我填充了地址和電話號碼。

Interceptor只負責處理本身的業務功能,好比咱們這裏是填充地址和手機號碼,而後本身的任務結束就會調用攔截器鏈條,執行鏈條接下去的任務,其餘跟Interceptor無關。

咱們來看咱們的攔截器和攔截器鏈條:

電話攔截器:

地址攔截器:

攔截器鏈:

Activity.java:

最後咱們能夠看到咱們的result結果爲:

這裏額外提下: 裏面的攔截器裏面的二個大步驟是能夠交換順序的,我先執行攔截鏈的方法,讓它提早去執行下一個攔截器的操做,再拿相應的返回值作我這個攔截器的操做。好比仍是剛纔那個電話攔截器,咱們調換了二個的順序:

這樣就會去先執行地址攔截器,而後拿到結果後再去處理電話攔截器的邏輯,因此最後的輸出結果爲:

這裏咱們懂了之後,咱們再去看Okhttp前面提到的攔截器添加,攔截鏈的相關代碼,是否是簡單的一比,它的連接鏈的操做跟咱們的基本架構一致,而後各自的攔截器無非就是處理各自的邏輯,對參數進行更改,發起請求等。因此咱們的核心變成了OkHttp的各個攔截器到底作了什麼邏輯。(也就是咱們提到的攔截器中的二個大操做的其中一步,本身的處理邏輯。)


原本想一步步的來寫每一個單獨的攔截器的做用,後來想了下,單獨攔截器的代碼分析的文章真的太多太多了。並且每一個攔截器寫的很簡單,其實沒啥大的意義,寫的仔細,一個攔截器就能夠是一篇文章,而咱們本文也側重於整體的源碼架構,因此我後面若是能夠的,都直接引用別人的文章了。


4.1 RetryAndFollowUpInterceptor

看名字就知道這個攔截器的做用是重試和重定向的。

你們能夠參考本文:

OKhttp源碼解析---攔截器之RetryAndFollowUpInterceptor

4.2 BridgeInterceptor

咱們來看BridgeInterceptor類的說明備註:

什麼?看不懂英文,谷歌翻譯走起:

簡單來講,咱們本身在Okhttp裏面創建了一個Request請求對象,可是這個對象並非直接就能夠用來立刻發送網絡請求的,畢竟咱們剛開始實例化Request的時候就簡單的放入了Url,body等,不少參數都是沒有設置的,因此咱們還須要補充不少參數,而後發起網絡請求,而後網絡返回的參數,咱們再把它封裝成Okhttp能夠直接使用的對象。

一句話歸納: 將客戶端構建的Request對象信息構建成真正的網絡請求;而後發起網絡請求,最後就是將服務器返回的消息封裝成一個Response對象

參考文章:

OkHttp之BridgeInterceptor簡單分析

4.3 CacheInterceptor

緩存攔截器,簡單來講就是有緩存就使用緩存。

參考文章:

Okhttp之CacheInterceptor簡單分析

4.4 ConnectInterceptor

鏈接攔截器,顧名思義打開了與服務器的連接,正式開啓了網絡請求。

由於之前在文章: Android技能樹 — 網絡小結(4)之socket/websocket/webservice 提到過,咱們的請求是經過Socket去訪問的。

因此最終這個ConnectInterceptor也會去發起一個Socket鏈接請求。

參考文章:

OkHttp之ConnectInterceptor簡單分析

4.5 CallServerInterceptor

咱們曾經在文章 Android技能樹 — 網絡小結(2)之TCP/UDP 提過:

TCP要先創建通道,而後再發送數據。

上面的攔截器ConnectInterceptor已經幫咱們把通道創建好了,因此在這個CallServerInterceptor攔截器裏面,咱們的任務就是發送相關的數據,

參考文章:

Okhttp之CallServerInterceptor簡單分析

4.6 自定義攔截器

咱們在流程圖中看到了,除了OKHttp源碼裏面自帶的攔截器,還有二種自定義攔截器,應用攔截器和網絡攔截器。

使用代碼:

okHttpClient = new OkHttpClient
                .Builder()
                .addInterceptor(appInterceptor)//Application攔截器
                .addNetworkInterceptor(networkInterceptor)//Network攔截器
                .build();
複製代碼

咱們知道網絡請求中間必定要通過一系列的攔截器,咱們也能夠本身寫攔截器,而後對裏面的參數作處理,好比咱們對Request在攔截器中作某個寫參數變動,而後再交給下一個攔截器。

而這二個自定義攔截器的位置,在咱們前面分析獲取攔截鏈的方法getResponseWithInterceptorChain中就提過了,如今再拿出來從新說一遍:

Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    '最早添加用戶的自定義APPlication攔截器'
    interceptors.addAll(client.interceptors());
    
    '而後是一系列的Okhttp自帶的攔截器'
    interceptors.add(retryAndFollowUpInterceptor);
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    interceptors.add(new CacheInterceptor(client.internalCache()));
    interceptors.add(new ConnectInterceptor(client));
    
    '在最終跟服務器交互數據的CallServerInterceptor前,添加用戶自定義的NetWork攔截器'
    '由於若是放在最後就沒什麼意義了。'
    if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(forWebSocket));

    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
        originalRequest, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    return chain.proceed(originalRequest);
  }
複製代碼

參考文章:

OkHttp基本使用(五)自定義攔截器

結語:

OkHttp源碼寫的也比較倉促,特別後面的各個攔截器的源碼分析就偷懶了,由於否則會引出一大段一大段的內容,就直接引用其餘大佬的文章。若是哪裏不對,歡迎你們指出。

相關文章
相關標籤/搜索