okhttp源碼分析

1. 項目結構

  • 攔截器
  • 連接池
  • 線程池
okhttp3/
└── internal
    ├── cache
    ├── cache2
    ├── connection
    ├── http
    ├── http1
    ├── http2
    ├── io
    ├── platform
    ├── publicsuffix
    ├── tls
    └── ws

2.執行流程圖

2.1代碼執行流程
image
2.2 正常流程
  • 通道建立與安全
    • dns
    • 是否安全連接(https, http)
    • 連接建立
  • 數據傳輸與解析
    • 發送請求
    • 接收響應
    • 釋放連接

完整的請求結束后,連接通道不會被立即關閉,而是會存活一段時間,當相同的請求在短時間內再次被觸發時, 連接會被復用,從而跳過連接過程

image
2.3 異常處理流程
  • 連接為建立時,直接觸發異常
  • 連接建立后, 關閉連接,觸發異常
image
2.4重試機制
  • 連接復用機制


    image

3.請求(Request)

3.1 參數列表
參數 必選 作用
url yes 請求地址
method no 請求方法,默認GET
header no 請求的頭部信息,擁有默認值
body no 請求體, 可以為空
3.2 方法

header(String name, String value)
使用該方法,會導致如果已經有key相同的鍵值對,該鍵值對會被新值替換

addHeader(String name, String value)
添加頭部鍵值對,不會覆蓋

cacheControl(CacheControl cacheControl)
響應的緩存控制,本質也是設置頭部信息

tag(Object tag)
設置該請求的標簽,通過該標簽可以使用okHttpClient取消本次請求

//工具方法
public class OkHttpUtils {
   public static void cancelCallWithTag(OkHttpClient client, String tag) {
       // A call may transition from queue -> running. Remove queued Calls first.
       for(Call call : client.dispatcher().queuedCalls()) {
           if(call.request().tag().equals(tag))
               call.cancel();
       }            
       for(Call call : client.dispatcher().runningCalls()) {
           if(call.request().tag().equals(tag))
               call.cancel();
       }
   }
}

4.響應

4.1 參數列表
參數 作用
code 響應狀態碼
message 請求結果簡單描述
header 響應的頭部
body 響應體
4.2 方法

protocol()
返回協議版本,枚舉類型

code()
響應的狀態碼

handshake()
請求的握手信息, 如果不是tls 協議,則返回空的

peekBody(long byteCount)
獲取部分響應,如果byteCount 大于全部的響應字節數, 則返回全部響應, 反之,則返回對應字節數的響應體

networkResponse()
獲取網絡響應

cacheResponse()
獲取緩存中的響應

priorResponse()
獲取上一個請求, 重定、向驗證、重連、這些操作

cacheControl()
控制緩存的

challenges()
當驗證未通過時,獲取驗證的詳細信息

sentRequestAtMillis()
獲取發送請求時的時間戳, 如果是緩存中的響應,那么這個時間戳就是原始請求的發送時間

receivedResponseAtMillis()
獲取接收響應的時間(接收頭部),如果是緩存的響應,那么這個時間戳就是原始接收響應的時間戳

5.攔截器

image
5.1應用級別

應用層級感知不到響應是否是緩存,緩存和網絡都可以攔截到

public class Test {
    public static void main(String[] args) {
        OkHttpClient client = new OkHttpClient.Builder()
                .addInterceptor(new LoggerInterceptor())
                .build();
        Request request = new Request.Builder()
                .url("http://www.baidu.com")
                .build();

        try (Response response = client.newCall(request).execute()) {
        } catch (Exception ignore) { }
    }

    private static class LoggerInterceptor implements Interceptor {
        @Override
        public Response intercept(Chain chain) throws IOException {
            //獲取請求對象
            Request request = chain.request();
            System.out.println(String.format("request >>> timestamp:%d, method:%s, url:%s, headers:%s, body:%s",
                    System.currentTimeMillis(), request.method(), request.url(), request.headers(), request.body()));
            //同步觸發請求
            Response response = chain.proceed(request);
            System.out.println(String.format("response >> timestamp:%d, code:%d, headers:%s, body:%s",
                    System.currentTimeMillis(), response.code(), response.headers(), request.body()));
            return response;
        }
    }
}

5.2網絡級別

只能攔截到網絡層級的,當強制使用緩存或者緩存未過期時,此時使用的是緩存,將攔截不到。

public class TestNetwork {
    public static void main(String[] args) {
        OkHttpClient client = new OkHttpClient.Builder()
                .addNetworkInterceptor(new LoggerInterceptor())
                .build();

        Request request = new Request.Builder()
                //強制使用緩存后,network層級無法捕獲
               // .cacheControl(CacheControl.FORCE_CACHE)
                .url("http://www.baidu.com")
                .build();

        try (Response response = client.newCall(request).execute()) {
        } catch (Exception ignore) { }
    }

    private static class LoggerInterceptor implements Interceptor {
        @Override
        public Response intercept(Chain chain) throws IOException {
            //獲取請求對象
            Request request = chain.request();
            System.out.println(String.format("request >>> timestamp:%d, method:%s, url:%s, headers:%s, body:%s",
                    System.currentTimeMillis(), request.method(), request.url(), request.headers(), request.body()));
            //同步觸發請求
            Response response = chain.proceed(request);
            System.out.println(String.format("response >> timestamp:%d, code:%d, headers:%s, body:%s",
                    System.currentTimeMillis(), response.code(), response.headers(), request.body()));
            return response;
        }
    }
}

6.鏈式調用

6.1 源碼分析
image
public final class RealInterceptorChain implements Interceptor.Chain {
    
  public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      RealConnection connection) throws IOException {
          ....
            //調用proceed時創建鏈條中的下一個節點,標記節點的方式是使用index腳標
            RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,connection, index + 1, request, call,eventListener,connectTimeout, readTimeout,writeTimeout);
            //獲取當前節點
            Interceptor interceptor = interceptors.get(index);
            //執行該節點任務代碼
            Response response = interceptor.intercept(next);
          ....
            return response;
      }
}


public final class RetryAndFollowUpInterceptor implements Interceptor {
      @Override public Response intercept(Chain chain) throws IOException {
          //獲取原始請求
           Request request = chain.request();
           //執行任務
          ...
           //調用proceed,獲取下一個節點,并執行下一個攔截器
           response = realChain.proceed(request, streamAllocation, null, null);
          ...
          
          //獲取響應后,執行后續任務
      }
}
6.2 模式分析

與傳統責任鏈相比,具有邊調用邊創建的特性

image

對于攔截器而言,干自己的事情,獲取原始的請求做自己的任務,然后傳遞出去。對于Chain而言,就是起到組織整個鏈子的目的,并標記自身位置

//基礎接口
public interface Interceptor {
    String interceptor(Chain chain);

    interface Chain {
        String request();
        String proceed(String request);
    }
}

//攔截器1
public class RetryAndFollowInterceptor implements Interceptor {
    @Override
    public String interceptor(Chain chain) {
        System.out.println("執行 RetryAndFollowInterceptor 攔截器之前代碼");
        String proceed = chain.proceed(chain.request());
        System.out.println("執行 RetryAndFollowInterceptor 攔截器之后代碼 得到最終數據:" + proceed);
        return proceed;
    }
}


//攔截器2
public class BridgeInterceptor implements Interceptor {
    @Override
    public String interceptor(Chain chain) {
        System.out.println("執行 BridgeInterceptor 攔截器之前代碼");
        String proceed = chain.proceed(chain.request());
        System.out.println("執行 BridgeInterceptor 攔截器之后代碼 得到最終數據:"+proceed);
        return proceed;
    }
}

//攔截器3
public class CacheInterceptor implements Interceptor {
    @Override
    public String interceptor(Chain chain) {
        System.out.println("執行 CacheInterceptor 最后一個攔截器 返回最終數據");
        return "success";
    }
}


//責任鏈的節點
public class RealInterceptorChain implements Interceptor.Chain {
    private List<Interceptor> mInterceptors;
    private String mRequest;
    private int mIndex;

    public RealInterceptorChain(List<Interceptor> interceptors, int index, String request) {
        this.mInterceptors = interceptors;
        mRequest = request;
        this.mIndex = index;
    }

    @Override
    public String request() {
        return mRequest;
    }

    @Override
    public String proceed(String request) {
        if (mIndex >= mInterceptors.size()) return null;
        //使用index變量標記當前責任鏈執行到何處
        RealInterceptorChain next = new RealInterceptorChain(mInterceptors, mIndex + 1, request);
        Interceptor interceptor = mInterceptors.get(mIndex);
        return interceptor.interceptor(next);
    }
}

//程序入口

public class Client {
    public static void main(String[] args) {
        List<Interceptor> interceptors = new ArrayList<>();
        interceptors.add(new BridgeInterceptor());
        interceptors.add(new RetryAndFollowInterceptor());
        interceptors.add(new CacheInterceptor());

        RealInterceptorChain realInterceptorChain = new RealInterceptorChain(interceptors, 0, "request");
        String ret = realInterceptorChain.proceed("request");
        System.out.println("result: " + ret);
    }
}

7.異步與同步請求

連接池

同步
final class RealCall implements Call {
      @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);
      //真正干活的在這里,調用攔截器,堵塞在這里等待請求
      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);
    }
  }
  
    Response getResponseWithInterceptorChain() throws IOException {
     //用戶自己設置的責任鏈
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());
    //負責處理失敗后的重試與重定向
    interceptors.add(retryAndFollowUpInterceptor);
    //將網絡請求Request裝換成網絡請求的數據
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    //緩存攔截器
    interceptors.add(new CacheInterceptor(client.internalCache()));
    //連接服務器 負責和服務器建立連接,真正的網絡請求(還沒發送數據)
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
      //添加用戶配置的網絡攔截器
      interceptors.addAll(client.networkInterceptors());
    }
    
    //寫入數據和獲取數據并解析成響應
    interceptors.add(new CallServerInterceptor(forWebSocket));

    //創建責任鏈
    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
        originalRequest, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    return chain.proceed(originalRequest);
  }
}
異步
final class RealCall implements Call {
      @Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    //關注一下這個回調,這里回調里面包含要執行的getResponseWithInterceptorChain函數
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }
}


public final class Dispatcher {
    //當前處于空閑狀態時, 處于
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      //添加到運行隊列,開始運行
      runningAsyncCalls.add(call);
      executorService().execute(call);
    } else {
     //放入到預備隊列
      readyAsyncCalls.add(call);
    }
}

 final class AsyncCall extends NamedRunnable {
         @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 {
          eventListener.callFailed(RealCall.this, e);
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }
 }

線程池與連接池

線程池
  • 等待隊列
  • 運行隊列
image

線程池代碼分析

public final class Dispatcher {
  private int maxRequests = 64;
  private int maxRequestsPerHost = 5;
  private @Nullable Runnable idleCallback;

  //線程管理
  private @Nullable ExecutorService executorService;

  //異步等待隊列
  private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();

  //異步執行隊列
  private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();

  //同步執行隊列,沒干啥,就是記錄
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
  
  //判斷實際的運行請求數是否小于允許的最大的請求數量(64) 并且共享主機的正在運行的調用的數量小于同時最大的相同Host的請求數(5)
    synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      //滿足,直接執行
      runningAsyncCalls.add(call);
      executorService().execute(call);
    } else {
      //存入等待執行的隊列
      readyAsyncCalls.add(call);
    }
  }
  
  //當任務完成的時候,會檢查等待隊列,并將等待隊列中的任務添加到執行隊列中
   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.
    }
  }
  
  
  //異步執行結束
  void finished(AsyncCall call) {
    finished(runningAsyncCalls, call, true);
  }

 //同步執行結束
  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 (!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();
    }
  }
  }

連接池

就是通過 host來判斷是否可復用連接,socket 不斷

public final class ConnectionPool {

  //一個定時清理的可執行對象, 清理掉過期的失效的連接
  private final Runnable cleanupRunnable = new Runnable() {
    @Override public void run() {
      while (true) {
        long waitNanos = cleanup(System.nanoTime());
        if (waitNanos == -1) return;
        if (waitNanos > 0) {
          long waitMillis = waitNanos / 1000000L;
          waitNanos -= (waitMillis * 1000000L);
          synchronized (ConnectionPool.this) {
            try {
              ConnectionPool.this.wait(waitMillis, (int) waitNanos);
            } catch (InterruptedException ignored) {
            }
          }
        }
      }
    }
  };

  //存儲隊列
   private final Deque<RealConnection> connections = new ArrayDeque<>();

 //比較重要的方法,獲取連接,實際上是遍歷所有存在的連接判斷host是否相同來復用連接,沒有直接返回null
 @Nullable RealConnection get(Address address, StreamAllocation streamAllocation, Route route) {
    assert (Thread.holdsLock(this));
    for (RealConnection connection : connections) {
      if (connection.isEligible(address, route)) {
        streamAllocation.acquire(connection, true);
        return connection;
      }
    }
    return null;
  }
}

待續。。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 那么我今天給大家簡單地講一下Okhttp這款網絡框架及其原理。它是如何請求數據,如何響應數據的 有什么優點?它的應...
    卓而不群_0137閱讀 322評論 0 1
  • 主目錄見:Android高級進階知識(這是總目錄索引)?OkHttp的知識點實在是不少,優秀的思想也有很多,這里只...
    ZJ_Rocky閱讀 2,320評論 2 6
  • 版本號:3.13.1 一.基本使用 Call可以理解為Request和Response之間的橋梁,Http請求過程...
    慕涵盛華閱讀 1,039評論 0 8
  • 如需轉載請評論或簡信,并注明出處,未經允許不得轉載 目錄 前言 okhttp是square開源的輕量級網絡框架 官...
    Geekholt閱讀 391評論 0 5
  • 1、說明 經翻譯之后的結果如下HTTP是現代應用網絡的方式。這是我們交換數據和媒體的方式。高效地執行HTTP可以使...
    Kevin_Lv閱讀 402評論 0 1