OkHttp源碼徹底解析(二)OkHttp架構及API源碼

 

 

 

前言

 

OkHttp是一個處理網絡請求的開源項目,是安卓端最火熱的輕量級框架,由移動支付Square公司貢獻(該公司還貢獻了Picasso)

用於替代HttpUrlConnection和Apache HttpClient(android API23 6.0裏已移除HttpClient,現在已經打不出來),這是現在非常主流的一個網絡請求框架了。

可能有人會說Retrofit+RxJava纔是最主流的,好吧,其實Retrofit的強大也是基於OkHttp,其是在OkHttp的基礎上進一步封裝,所以OkHttp也是切入Retrofit源碼學習的入口。

博主對Retrofit2.x和OkHttp3.0也是比較熟悉的,剛好最近比較有空,接着週末時間總結了OkHttp。

本系列將帶領大家從源碼的出發,做到儘可能詳細地剖析OkHttp的每一個知識點。
 

該系列的第一篇文章中我們已經瞭解了OkHttp從發起一個請求到收到網絡回調數據的流程(Request——>Response)的過程。

本文的主要是從源碼出發,帶大家瞭解從發起一個Request請求到調用Dispatcher(分發器)調用線程池,來實現同/異步發起請求數據的流程,及內部涉及的設計模式和原理。

 

 

本系列文章:

OkHttp源碼徹底解析(一)OkHttp請求流程

OkHttp源碼徹底解析(二)OkHttp架構及API源碼

OkHttp源碼徹底解析(三)OkHttp3.0攔截器原理——責任鏈模式

OkHttp源碼徹底解析(四)OkHttp攔截器的作用

OkHttp源碼徹底解析(五)OkHttp連接池

 


目錄

前言

客戶端請求的數據Request、服務端返回的數據Response——Builder模式

Request:

Response:

okHttpClient——外觀模式,組合模式

newCall

call

dispatcher與executorService

執行請求

Converter工廠模式


 

OkHttp整體流程

這是OkHttp請求的流程,也是本章講解的流程,圖中省略處爲攔截器部分(責任鏈模式)本章涉及,將在下一篇博客提到

 

 

我們都知道,要是有網絡請求的API之前,必須先有請求的信息,也就是request

客戶端請求的數據Request、服務端返回的數據Response——Builder模式

 

首先要明白,Requset與Response爲什麼使用Builder模式

  • 因爲它們需要的參數滿足這兩點中的一點:
    • 1.參數多且雜 ; 2.參數不是必須要傳入的
  • 值得注意的一點是:數據請求類 的 url 是必須傳的,會在 build() 方法裏檢查,如果 爲空會報異常

Request:

這是一個 請求數據 的封裝類(封裝了請求頭、請求地址等等)

public final class Request {
      //url字符串和端口號信息,默認端口號:http爲80,https爲443.其他自定義信息
      private final HttpUrl url;
      
      //"get","post","head","delete","put"....
      private final String method;
      
      //包含了請求的頭部信息,name和value對。最後的形勢爲:$name1+":"+$value1+"\n"+ $name2+":"+$value2+$name3+":"+$value3...
      private final Headers headers;
      
      //請求的數據內容
      private final RequestBody body;
      
      //請求的附加字段。對資源文件的一種摘要。保存在頭部信息中:ETag: "5694c7ef-24dc"。客戶端可以在二次請求的時候,在requst的頭部添加緩存的tag信息(如If-None-Match:"5694c7ef-24dc"),服務端用改信息來判斷數據是否發生變化。
      private final Object tag;
      
      //各種附值函數和Builder類
      ...
        
     }

其中,ResponseBody是請求的具體內容,是抽象類

public abstract class RequestBody {
     
      ...
      
      //返回內容類型
      public abstract MediaType contentType();
    
      //返回內容長度
      public long contentLength() throws IOException {
        return -1;
      }
    
      //如何寫入緩衝區。BufferedSink是第三方庫okio對輸入輸出API的一個封裝,不做詳解。
      public abstract void writeTo(BufferedSink sink) throws IOException;

    }

OKHttp3中給出了兩個requestBody的實現FormBody 和 MultipartBody,分別對應了兩種不同的MIME類型:"application/x-www-form-urlencoded"和"multipart/"+xxx.作爲的默認實現

其中,有一個重要的抽象方法writeTo

public abstract void writeTo(BufferedSink sink) throws IOException;

該方法的參數BufferedSink是Okio的封裝,就是一個sink就是從本地寫出的特殊的IO流。

這個抽象方法會在最後一個攔截器CallServerInterceptor裏面,也就是最終發起網絡請求的部分被調用

request.body().writeTo(bufferedRequestBody);

把request裝換成bufferedRequestBody,並作爲IO流通過Socket寫到目標網絡中,當然,在這一步之前還有好多好多好多操作,這裏簡單先提一下。

 

 

Response:

public final class Response implements Closeable {
      //網絡請求的信息
      private final Request request;
      
      //網路協議,OkHttp3支持"http/1.0","http/1.1","h2"和"spdy/3.1"
      private final Protocol protocol;
      
      //返回狀態碼,包括404(Not found),200(OK),504(Gateway timeout)...
      private final int code;
      
      //狀態信息,與狀態碼對應
      private final String message;
      
      //TLS(傳輸層安全協議)的握手信息(包含協議版本,密碼套件(https://en.wikipedia.org/wiki/Cipher_suite),證書列表
      private final Handshake handshake;
      
      //相應的頭信息,格式與請求的頭信息相同。
      private final Headers headers;
      
      //數據內容在ResponseBody中
      private final ResponseBody body;
      
      //網絡返回的原聲數據(如果未使用網絡,則爲null)
      private final Response networkResponse;
      
      //從cache中讀取的網絡原生數據
      private final Response cacheResponse;
      
      //網絡重定向後的,存儲的上一次網絡請求返回的數據。
      private final Response priorResponse;
      
      //發起請求的時間軸
      private final long sentRequestAtMillis;
      
      //收到返回數據時的時間軸
      private final long receivedResponseAtMillis;
    
      //緩存控制指令,由服務端返回數據的中的Header信息指定,或者客戶端發器請求的Header信息指定。key:"Cache-Control"
      //詳見<a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9">RFC 2616,14.9</a>
      private volatile CacheControl cacheControl; // Lazily initialized.
        
      //各種附值函數和Builder類型          ...
    }

其中,上面比較重要的是:1.ResponseBody是獲取的數據內容,2.三個Response:網絡返回的、從cache中讀取的、重定向後保存的之前的網絡請求返回數據,3.Requset網絡請求信息,4.Headers響應頭:可以知道緩存指令,5.code狀態碼:404就是錯誤,6.CacheControl 緩存控制指令,由服務器返回的Header或客戶端Header指定

介紹了Request和Response之後,我們來了解如何通過這個Requset來得到Pesronse,已經這裏面API的內部邏輯

 

 

 

 

 

首先,OKHttp3在項目中發起網絡請求的API如下:

okHttpClient.newCall(request).execute();

我們按順序來了解這個API涉及的類的源碼:

 

 

 

okHttpClient——外觀模式,組合模式

OkHttp是一個比較龐大的網絡請求框架(Retrofit內部也是使用OkHttp這個框架),爲了方便地和這個框架內部複雜的子模塊進行交互,OkHttpClient使用了外觀模式來實現。將OKHttp的很多功能模塊,全部包裝進這個類中,讓這個類單獨提供對外的API,這種設計叫做外觀模式。將操作都隱藏起來,減少用戶的交互成本。

由於內部功能模塊太多,使用了Builder模式(生成器模式)來構造。

它的方法只有一個:newCall.返回一個Call對象(一個準備好了的可以執行和取消的請求)。

newCall

先來看源碼:

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

我們可以看到,newCall其實是返回一個RealCall類,也就是說我們的同/異步請求網絡數據,實際上都是調用這個RealCall的execute/enqueue方法。這是一個Call接口的實現類

call

public interface Call {
  
  Request request();
 
  //同步的方法,直接返回Response
  Response execute() throws IOException;
  
  //異步的,傳入回調CallBack即可(接口,提供onFailure和onResponse方法)
  void enqueue(Callback responseCallback);
  
  void cancel();

  boolean isExecuted();

  boolean isCanceled();

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

Call接口提供了內部接口Factory(用於將對象的創建延遲到該工廠類的子類中進行,從而實現動態的配置,工廠方法模式)。

 

dispatcher與executorService

dispatcher與executorService分別是分發器與線程池,承接上面的RealCall,

OKHttpClient類中有個成員變量dispatcher負責請求的分發。既在真正的請求RealCall的execute方法中,使用dispatcher來執行任務:

  • RealCall的execute方法:

      @Override 
      public Response execute() throws IOException {
      synchronized (this) {
        if (executed) throw new IllegalStateException("Already Executed");
        executed = true;
      }
      try {
        //使用dispatcher 來分發任務
        client.dispatcher().executed(this);
        Response result = getResponseWithInterceptorChain();
        if (result == null) throw new IOException("Canceled");
        return result;
      } finally {
        client.dispatcher().finished(this);
      }
      }
    
  • RealCall的enqueue方法:

      @Override public void enqueue(Callback responseCallback) {
          synchronized (this) {
            if (executed) throw new IllegalStateException("Already Executed");
            executed = true;
          }
          //使用dispatcher來將人物加入隊列
          client.dispatcher().enqueue(new AsyncCall(responseCallback));
        }
    

OKHttp3中分發器只有一個類 ——Dispathcer.

也就是說,enqueue/execute(同/異步)都是內部調用了dispatcher來執行任務,

 

同步操作:dispathcer調用自己的execute方法

流程:

1.在RealCall中client.dispatcher().execute(this); 其中this就是RealCall

2.executorService()就是獲取一個線程池

3.RealCall的execute內部是executorService().execute(this);線程池executorService調用他的execute(call)   ( call也就是上面的new AsyncCall(responseCallback))

 

異步操作:dispathcer調用自己的enqueue方法

流程:

 1.在RealCall中client.dispatcher().enqueue(new AsyncCall(responseCallback));其中,AsyncCall與上面同步時的RealCall形對比,同步調用RealCall,異步調用RealCall的內部類AsyncCall的enqueue

2.executorService()方法獲取一個線程池,而

3.AsyncCall的enqueue內部是executorService().execute(call);線程池executorService調用他的execute(call)   ( call也就是上面的new AsyncCall(responseCallback))

 

 

下面我們可以通過源碼來看看Dispatcher

線程池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;
  }

參數:

  • 0:核心線程數量。保持在線程池中的線程數量(即使已經空閒),爲0代表線程空閒後不會保留,等待一段時間後停止。
  • Integer.MAX_VALUE: 線程池可容納線程數量。
  • 60,TimeUnit.SECONDS: 當線程池中的線程數大於核心線程數時,空閒的線程會等待60s後纔會終止。如果小於,則會立刻停止。
  • new SynchronousQueue<Runnable>():線程的等待隊列。同步隊列,按序排隊,先來先服務。
    Util.threadFactory("OkHttp Dispatcher", false): 線程工廠,直接創建一個名爲 “OkHttp Dispathcer”的非守護線程。

(2) 執行同步的Call:直接加入runningSyncCalls隊列中,實際上並沒有執行該Call,交給外部執行。

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

(3) 將Call加入隊列:如果當前正在執行的call數量大於maxRequests,64,或者該call的Host上的call超過maxRequestsPerHost,5,則加入readyAsyncCalls排隊等待。否則加入runningAsyncCalls,並執行。

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

(4) 從ready到running的輪轉,在每個call 結束的時候調用finished,並:

    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!");
          //每次remove完後,執行promoteCalls來輪轉。
          if (promoteCalls) promoteCalls();
          runningCallsCount = runningCallsCount();
          idleCallback = this.idleCallback;
        }
        //線程池爲空時,執行回調
        if (runningCallsCount == 0 && idleCallback != null) {
          idleCallback.run();
        }
      }

(5) 線程輪轉:遍歷readyAsyncCalls,將其中的calls添加到runningAysncCalls,直到後者滿。

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

執行請求

同步的請求RealCall 實現了Call接口:
可以execute,enqueue和cancle。
異步的請求AsyncCall(RealCall的內部類)實現了Runnable接口:
只能run(調用了自定義函數execute).

execute 對比:

  • RealCall:

    @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 {
        //執行完畢,finish
        client.dispatcher().finished(this);
      }
    }
    
  • AsyncCall:

    @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) {
              Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
            } else {
              responseCallback.onFailure(RealCall.this, e);
            }
          } finally {
            //執行完畢,finish
            client.dispatcher().finished(this);
          }
        }

實際上的執行函數都是getResponseWithInterceptorChain():

    private Response getResponseWithInterceptorChain() throws IOException {
        //創建一個攔截器列表
        List<Interceptor> interceptors = new ArrayList<>();
        //優先處理自定義攔截器
        interceptors.addAll(client.interceptors());
        //失敗重連攔截器
        interceptors.add(retryAndFollowUpInterceptor);
        //接口橋接攔截器(同時處理cookie邏輯)
        interceptors.add(new BridgeInterceptor(client.cookieJar()));
        //緩存攔截器
        interceptors.add(new CacheInterceptor(client.internalCache()));
        //分配連接攔截器
        interceptors.add(new ConnectInterceptor(client));
        //web的socket連接的網絡配置攔截器
        if (!retryAndFollowUpInterceptor.isForWebSocket()) {
          interceptors.addAll(client.networkInterceptors());
        }
        //最後是連接服務器發起真正的網絡請求的攔截器
        interceptors.add(new CallServerInterceptor(
            retryAndFollowUpInterceptor.isForWebSocket())); 
        Interceptor.Chain chain = new RealInterceptorChain(
            interceptors, null, null, null, 0, originalRequest);
        //流式執行並返回response
        return chain.proceed(originalRequest);
      }

 

 

這裏額外提一下converter

 

Converter工廠模式

converter:序列化,反序列化的工具(對應Requset和Response),實現數據類型的轉換,例Gson解析等

converterFactory是converter的工廠模式,用來構建各種converter,

 

可以添加converterFactory由retrofit完成requestBody和responseBody的構造。

這裏對retrofit2不展開討論,後續會出新的文章來詳細討論。僅僅介紹一下converterFacotry,以及它是如何構建OkHttp3中的RequestBody和ResponseBody的。

  • Note: retrofit2中的Response與okhttp3中的response不同,前者是包含了後者。既retrofit2中的response是一層封裝,內部纔是真正的okhttp3種的response。

我們項目中的一個converterFacotry代碼如下:

    public class RsaGsonConverterFactory extends Converter.Factory {
   
    //省略部分代碼
    ...
    
    private final Gson gson;

    private RsaGsonConverterFactory(Gson gson) {
        if (gson == null) throw new NullPointerException("gson == null");
        this.gson = gson;
    } 
    //將返回的response的Type,註釋,和retrofit的傳進來,返回response的轉換器。Gson只需要type就可以將responseBody轉換爲需要的類型。
    @Override
    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
        TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
        return new RsaGsonResponseBodyConverter<>(gson, adapter);
    }
    //將request的參數類型,參數註釋,方法註釋和retrofit傳進來,返回request的轉換器。Gson只需要type就可以將request對象轉換爲OKHttp3的reqeustBody類型。
    @Override
    public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
        TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
        return new RsaGsonRequestBodyConverter<>(gson, adapter);
    }
    }

該Factory(工廠方法模式,用於動態的創建對象)主要是用來生產response的converter和request的converter。顯然我們使用了Gson作爲數據轉換的橋樑。分別對應如下兩個類:

  • response的converter(之所以命名爲Rsa,是做了一層加解密):

      public class RsaGsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
          private final Gson gson;
          private final TypeAdapter<T> adapter;
      
          RsaGsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
              this.gson = gson;
              this.adapter = adapter;
          }
      
          @Override public T convert(ResponseBody value) throws IOException {
              JsonReader jsonReader = gson.newJsonReader(value.charStream());
              try {
                  return adapter.read(jsonReader);
              } finally {
                  value.close();
              }
          }
      }
    

直接將value中的值封裝爲JsonReader供Gson的TypeAdapter讀取,獲取轉換後的對象。

  • request的converter:

      final class RsaGsonRequestBodyConverter<T> implements Converter<T, RequestBody> {
          private static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=UTF-8");
          private static final Charset UTF_8 = Charset.forName("UTF-8");
      
          private final Gson gson;
          private final TypeAdapter<T> adapter;
      
          RsaGsonRequestBodyConverter(Gson gson, TypeAdapter<T> adapter) {
              this.gson = gson;
              this.adapter = adapter;
          }
      
          @Override public RequestBody convert(T value) throws IOException {
      
              Buffer buffer = new Buffer();
              Writer writer = new OutputStreamWriter(buffer.outputStream(), UTF_8);
              JsonWriter jsonWriter = gson.newJsonWriter(writer);
      
              adapter.write(jsonWriter, value);
              jsonWriter.close();
              //如果是RsaReq的子類,則進行一層加密。
              if(value instanceof RsaReq){
                 //加密過程
              }
              //不需要加密,則直接讀取byte值,用來創建requestBody
              else {
                  //這個構造方法是okhttp專門爲okio服務的構造方法。
                  return RequestBody.create(MEDIA_TYPE, buffer.readByteString());
              }
          }
      }   
    

上面的流操作使用的是第三方庫okio。可以看到,retrofitokhttp,okio這三個庫是完全相互兼容並互相提供了專有的API。



 

好了,這裏就是本章內容,本章介紹了OkHttp的架構,API內部的源碼,我們可以看到裏面包含了許多值得學習的設計模式,

Requset和Response的builder模式

converter的工廠模式

OkHttpClient的外觀模式,組合模式

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章