天天看點

Okhttp3源碼

Okhttp3源碼

hi,大家好,我是愛吃香蕉的猴子,Okhttp3的源碼之前沒有怎深入了解,最近看了很多Okhttp3源碼的文章,看的雲裡霧裡,在這我也不貼連接配接了,整理一篇自己了解的吧。

源碼Githup位址 具體的使用過程上面也有介紹,現在步入正題。

從使用步驟切入,再進入源碼,最後總結

OkHttpClient client = new OkHttpClient.Builder().build();
        Request request = new Request.Builder().
                url("https://github.com/square/okhttp").
                build();
        Call call = client.newCall(request);
        try {
            Response response = call.execute();
            call.enqueue(new Callback() {//異步
                @Override
                public void onFailure(Call call, IOException e) {
                }
                @Override
                public void onResponse(Call call, Response response) throws IOException {
                }
            });
        } catch (IOException e) {
            e.printStackTrace();
        }
           

上面的代碼是規規矩矩的使用範例,現在我們逐一解析

  • ①OkHttpClient client = new OkHttpClient.Builder().build();
  • 第一步聲明client對象,如何聲明呢? 為什麼通過Builder().builder()?
  • 那肯定要看OkHttpClient的代碼; path: OkHttp\okhttp\okhttp\src\main\java\okhttp3\OkHttpClient.java
public Builder() {
      dispatcher = new Dispatcher();//重點 分析Dispatcher
      protocols = DEFAULT_PROTOCOLS;//預設支援的協定
      connectionSpecs = DEFAULT_CONNECTION_SPECS;//預設的連接配接規範
      eventListenerFactory = EventListener.factory(EventListener.NONE);
      proxySelector = ProxySelector.getDefault();//預設的代理選擇器,直連
       ........
           
  • 在這個Builder構造器中,初始化了dispatcher, 在看一下build方法
public OkHttpClient build() {
      return new OkHttpClient(this);
    }
           

這樣就聲明了client對象,并初始化了dispatcher等等.

  • ②Request request = new Request.Builder().url().build();
  • 這一步就是初始化request; 方法和OkhttpClient很一樣, 注釋的大概意思:一個HTTP請求。我了解為對請求連結的處理吧
  • 然後主要就是要将request(資料請求連接配接)放入newCall中。
public Builder() {
      this.method = "GET"; //get的請求方式
      this.headers = new Headers.Builder();
    }
           
  • ③Call call = client.newCall(request);
  • 上面兩步的執行,就是為第三步做準備的,現在 在okhttpclient中newCall方法聲明了call對象.
/**
   * Prepares the {@code request} to be executed at some point in the future.
   */
  @Override public Call newCall(Request request) {
    return RealCall.newRealCall(this, request, false /* for web socket */);
  }
           
  • Call是一個interface, RealCall是真正的實作類,可以着重看RealCall;
  • path: OkHttp\okhttp\okhttp\src\main\java\okhttp3\RealCall.java
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.transmitter = new Transmitter(client, call);
    return call;
  }
           

這樣就是初始化了call, 這樣就要開始異步請求的工作;

  • ④ call.enqueue(new Callback() {
@Override public void enqueue(Callback responseCallback) {
     。。。。
    //client.dispatcher()傳回一個Dispatcher對象,異步請求排程器,裡面會對任務進行分辨,是立刻執行還是放入等待隊列
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }
           
  • 最後的代碼中參數new AsyncCall(responseCallback); 我們來猜一下 這個類是幹嘛的,我的直覺是和Runnable有關,因為畢竟異步請求嘛
  • final class AsyncCall extends NamedRunnable { //果然繼承NameRunnable
  • 這樣RealCall的代碼暫時看到這裡,現在看Dispatcher的enqueue方法;

path: OkHttp\okhttp\okhttp\src\main\java\okhttp3\Dispatcher.java

  • 先看一些變量
private int maxRequests = 64;//最大并發數為64,同時請求
  private int maxRequestsPerHost = 5;//每個主機的最大請求數為5
  private @Nullable Runnable idleCallback;//閑置接口
  /** Executes calls. Created lazily. */
  private @Nullable ExecutorService executorService;//線程池
  /** Ready async calls in the order they'll be run. */
  //緩存好的異步調用,都是放在隊列裡儲存
  private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
  /** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
  //運作中的異步調用,都是放在隊列裡儲存
  private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
  /** Running synchronous calls. Includes canceled calls that haven't finished yet. */
  //運作中的同步調用,都是放在隊列裡儲存
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
           
void enqueue(AsyncCall call) {
    synchronized (this) {
    readyAsyncCalls.add(call);//緩存好的異步調用,都是放在隊列裡儲存
      。。。
    //TODO 
    promoteAndExecute();//這一步就是增加線程池
  }
           

進入promoteAndExecute看一下

private boolean promoteAndExecute() {
        。。。。。
        //如果目前運作的異步請求隊列長度小于最大請求數,也就是64,并且主機的請求數小于每個主機的請求數也就是5,
        //則把目前請求添加到 運作隊列,接着交給線程池ExecutorService處理,否則則放置到readAsyncCall進行緩存,等待執行
        if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
        if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; // Host max capacity.
    。。。。。
    for (int i = 0, size = executableCalls.size(); i < size; i++) {
      AsyncCall asyncCall = executableCalls.get(i);
      //進入線程池
      asyncCall.executeOn(executorService());
    }
    return isRunning;
  }
           
  • 網絡請求資料,增加線程池 提高效率,進入線程池看一下–> executorService;
//同步最大的差別
  public synchronized ExecutorService executorService() {
    if (executorService == null) {
      executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
          new SynchronousQueue<>(), Util.threadFactory("OkHttp Dispatcher", false));
    }
    return executorService;
  }
           
  • 小結:添加到線程池後就交給ThreadPoolExecutor去調用,最終則是調用到我們的請求AsyncCall的execute方法。回看上面的代碼,異步請求中,我們傳遞了個Callback接口進來,而在RealCall的enqueue方法中,Callback回調接口被封裝到AsyncCall中,而AsyncCall繼承與NamedRunnable,而NamaedRunnable則實作了Runnable方法。
  • 現在,傳回到RealCall中檢視executeOn方法;

    path: OkHttp\okhttp\okhttp\src\main\java\okhttp3\RealCall.java # 内部類:AsyncCall

/**
     * Attempt to enqueue this async call on {@code executorService}. This will attempt to clean up
     * if the executor has been shut down by reporting the call as failed.
     */
    void executeOn(ExecutorService executorService) {
      assert (!Thread.holdsLock(client.dispatcher()));
      boolean success = false;
      try {
        executorService.execute(this);
           

進入executorService.execute(this);檢視

@Override protected void execute() {
      boolean signalledCallback = false;
      transmitter.timeoutEnter();
      try {
        //重點: 看OkHttp如何連接配接傳回
        Response response = getResponseWithInterceptorChain();
        signalledCallback = true;
        responseCallback.onResponse(RealCall.this, response);
           

現在要對傳回值處理: getResponseWithInterceptorChain

Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());
    interceptors.add(new RetryAndFollowUpInterceptor(client));
    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, transmitter, null, 0,
        originalRequest, this, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());
           
  • 這樣就分析到了,Okhttp的關鍵部分攔截器

    使用者自定義連接配接器

RetryAndFollowUpInterceptor:在連接配接失敗後進行重新連接配接,必要時進行重定向,如果調用被取消,可能會抛出IOException

BridgeInterceptor:建構通路網絡的橋梁,首先,将使用者請求轉換成網絡請求,然後通路網絡,最後将網絡響應轉換成使用者響應。

CacheInterceptor:緩存攔截器,從緩存中擷取伺服器請求,或者把伺服器響應寫入緩存中。

ConnectInterceptor:打開一個連接配接,去連接配接目标伺服器。

CallServerInterceptor:攔截器鍊中的最後一個鍊點,通過網絡請求伺服器。