(Android) OkHttp3.10 源碼學習筆記 1 同步請求分析

OkHttp的請求分爲同步請求和異步請求,下面我們先來分析同步請求的執行流程。

首先,看一下OkHttp執行同步請求的簡單示例:

OkHttpClient client = new OkHttpClient();

        Request request = new Request.Builder()
                .url("http://baidu.com")
                .build();

        Response response;
        try {
            response = client.newCall(request).execute();
            Log.d(TAG, response.body() != null ? response.body().toString() : null);
        } catch (IOException e) {
            e.printStackTrace();
        }

第一步,我們new了一個OkHttpClient,使用了默認的構造函數。

public OkHttpClient() {
    this(new Builder());
  }

這裏返回了一個new Builder(). 這個大家應該很熟悉,一個Builder模式的寫法。跟進源碼部分,可以看到它主要做了一些成員變量的初始化工作。比如Dispatcher,是請求分發器,連接池管理的connectionPool, 我們後面將重點分析, 以及一些timeout的設置等。

public Builder() {
      dispatcher = new Dispatcher();
      protocols = DEFAULT_PROTOCOLS;
      connectionSpecs = DEFAULT_CONNECTION_SPECS;
      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;
      connectTimeout = 10_000;
      readTimeout = 10_000;
      writeTimeout = 10_000;
      pingInterval = 0;
    }

第二步,下面我們看Request的初始化過程

Request request = new Request.Builder()
                .url("http://baidu.com")
                .build();

跟進Builder()方法,主要做了兩件簡單的事,默認爲GET方式,初始化了Header,保存頭部信息,傳入的url String構造HttpUrl對象。

 public Builder() {
      this.method = "GET";
      this.headers = new Headers.Builder();
    }

再看Build()方法,直接用之前的Builder對象,構造一個request。

public Request build() {
      if (url == null) throw new IllegalStateException("url == null");
      return new Request(this);
    }
Request(Builder builder) {
    this.url = builder.url;
    this.method = builder.method;
    this.headers = builder.headers.build();
    this.body = builder.body;
    this.tag = builder.tag != null ? builder.tag : this;
  }
第三步,通過下面的方法,得到response
response = client.newCall(request).execute();

我們看看call的創建過程

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

Call是一個接口,RealCall是Call的具體實現。這邊直接使用RealCall類的靜態方法newRealCall創建了一個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對象,賦值了一個listener。繼續看RealCall的構造方法

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

可以看出,RealCall持有了之前構造的client request對象,賦值了一個重定向攔截器,關於攔截器,後面也會具體分析。

最後,執行call.excute()方法,繼續看具體實現類RealCall

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

首先,判斷了excuted標誌位是否爲true,因爲同一個請求只能執行一次,如果已經執行了 就拋出異常,沒執行過,就把標誌位置爲true。

然後,捕捉call堆棧,並且執行了listener的callStart方法,看一下。

/**
   * Invoked as soon as a call is enqueued or executed by a client. In case of thread or stream
   * limits, this call may be executed well before processing the request is able to begin.
   *
   * <p>This will be invoked only once for a single {@link Call}. Retries of different routes
   * or redirects will be handled within the boundaries of a single callStart and {@link
   * #callEnd}/{@link #callFailed} pair.
   */
  public void callStart(Call call) {
  }

註釋上可以看出,每次執行call的enqueued或execute方法時,就會開啓這個listener。往下看核心代碼

 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);
    }
首先,執行了dispatcher的exexuted方法,dispatcher是OkHttp的請求分發器,對於同步請求,executed也比較簡單,只是把call放入了running這個隊列
  synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
  }

其實Dispatcher的作用,就是維持call請求發給他的狀態,然後內部維護了一個線程池,用於執行網絡請求。call在執行的過程中,通過dispatcher這個分發器類,把任務推到執行隊列當中,進行操作,操作完成,再執行等待隊列的任務。Dispatcher是OkHttp的核心之一,後面會具體分析。



接下來我們繼續代碼,同步請求隊列的定義,下面代碼中 的第三個。上面兩個,一個是異步的就緒隊列,一個是異步的執行隊列。

  /** 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<>();

執行完dispatcher.executed()方法後,會通過getResponseWithInterceptorChain()方法得到response。這個其實是一個攔截器鏈方法,後面也會具體分析。

最後我們看一下finally方法,他會在請求結束後執行finished方法

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

可以看出,把我們正在執行的同步請求傳入,從當前隊列移除同步請求。這邊主要第三個參數我們傳入的是false,這裏是走不到promoteCalls()的,但異步請求會走到這裏,到時再分析。然後,計算了目前還在運行的請求。最後兩行有一個判斷,如果runningCallsCount爲0且idleCallback不爲null時,運行了idleCallback。


最後,我們總結一下同步請求

1.創建一個OkHttpClient對象

2.構建一個request對象,通過OkHttpClient和Request對象,構建出Call對象

3.執行call的execute方法。




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