深刻OKHttp源碼分析(一)----同步和異步請求流程和源碼分析

好久沒有寫過博客了,一直認爲本身的技術實力不夠,怕寫了技術博客產生錯誤,誤導了你們,讓你們在錯誤的方向越走越遠,不能解決本身的問題,前段時間看了某大佬的關於寫做的分享,我決定從今天開始將本身在Android開發路上學習和解決問題的歷程記錄下來,若是博客中有錯誤的地方,也歡迎你們指正,咱們共同進步共同窗習。今天從OKhttp的源碼分析開始! ###OKhttp框架流程 基本的執行流程以下: OKhttpClient->Request->RealCall->Dispatcher->interceptors(RetryAndFollow->Bridge->Cache->Connect->CallServer)web

名詞解釋: #####OkHttpClient: 按照類文件中的說明就是發送HTTP請求和讀取響應的實例。也是Call的生產工廠 #####Request : HTTP請求 #####RealCall :真正的Call對象。 #####Dispatcher : 任務調度器 #####interceptors : 攔截器,其中包含RetryAndFollow、Bridge、Cache、Connect、CallServer,攔截器也能夠自定義bash

####OkHttp同步請求流程和源碼分析cookie

OkHttpClient client=new OkHttpClient.Builder().readTimeout(5, TimeUnit.SECONDS).build();

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

        Call call = client.newCall(request);

        try {
            Response response = call.execute();
        } catch (IOException e) {
            e.printStackTrace();
        }
複製代碼

首先咱們來看下OkHttpClient實例的建立,OkHttpClient實例是經過建立者模式來建立的,咱們來看下其源碼框架

public Builder() {
      dispatcher = new Dispatcher();//這裏建立了OKhttp的任務調度器對象
      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;
    }
複製代碼

經過上面的代碼來看,這個方法裏面主要是基本的賦值操做和任務調度器對象和鏈接池對象的建立,咱們繼續往下看build方法異步

public OkHttpClient build() {
      return new OkHttpClient(this);
    }
OkHttpClient(Builder builder) {
    this.dispatcher = builder.dispatcher;
    this.proxy = builder.proxy;
    this.protocols = builder.protocols;
    this.connectionSpecs = builder.connectionSpecs;
    this.interceptors = Util.immutableList(builder.interceptors);
    this.networkInterceptors = Util.immutableList(builder.networkInterceptors);
    this.eventListenerFactory = builder.eventListenerFactory;
    this.proxySelector = builder.proxySelector;
    this.cookieJar = builder.cookieJar;
    this.cache = builder.cache;
    this.internalCache = builder.internalCache;
    this.socketFactory = builder.socketFactory;
    ···//篇幅緣由,這裏就將剩下的代碼註釋啦。
  }
複製代碼

在build方法中直接返回了新建立的OkHttpClient對象,將Builder對象傳入OkHttpClient中,並在OkHttpClient的構造方法中進行賦值操做,此時,OkHttpClient的實例對象建立完成。socket

接着咱們看下請求對象Request的建立過程:async

Request request=new Request.Builder().url("http://www.baidu.com").build();
複製代碼

也和OkHttpClient的建立過程同樣,也是使用了建立者模式,咱們來看下Builder方法ide

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

在這裏咱們能夠看到,默認狀況下,請求方式爲GET方式,而後建立了header對象,用來存儲HTTP的請求頭數據,url方法就是設置url地址的,咱們就不跳進去看代碼了,咱們看下build方法函數

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;
  }
複製代碼

在build方法中,進行了url的判空操做,而後將Builder對象傳入Request類的構造函數,返回新建立的Request對象 在Request的構造函數中,只是進行簡單的賦值操做。此時Request對象也已經建立完成。源碼分析

接着進行第三步了,建立Call對象

Call call = client.newCall(request);
複製代碼

咱們點擊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對象的真正建立是在RealCall的newRealCall方法裏的,咱們進去看看

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;
  }
private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    this.client = client;
    this.originalRequest = originalRequest;
    this.forWebSocket = forWebSocket;
    this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
  }
複製代碼

在newRealCall方法中咱們看到,RealCall的建立是經過構造函數建立的,咱們點進來繼續看,裏面只是一些簡單的賦值操做,可是,請注意,最後一頁是咱們在前面簡介過的重試攔截器RetryAndFollowUpInterceptor的建立。這裏咱們先不詳細的介紹,後面再詳細講解。 在newRealCall方法中的最後一行是建立Call對象的事件監聽對象。 到此爲止,Call對象建立成功。 咱們繼續往下看,最後一步,

Response response = call.execute();
複製代碼

調用call的execute()方法,獲得咱們想要的response對象,咱們進入到execute方法中看看裏面是如何獲得咱們要的response對象的。

@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);
    }
  }
複製代碼

咱們直接來看關鍵代碼,client.dispatcher().executed(this);,咱們看下dispatcher方法,

public Dispatcher dispatcher() {
    return dispatcher;
  }
複製代碼

能夠看到,這裏直接返回了以前在構造器中建立的dispatcher對象,咱們接着看execute方法,

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

這個runningSynCalls是什麼鬼呢,咱們看下定義位置

/** 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<>(); 複製代碼

能夠看到,這個是一個存放尚未執行完畢的同步任務的隊列,咱們還看到了另外兩個隊列readyAsyncCalls、runningAsyncCalls,一個是待執行的異步任務的隊列,一個是正在執行的異步任務的隊列。這兩個實例咱們在後面的異步請求中會用到,這裏先跳過。 咱們回頭看下executed方法,這裏只是將Call對象添加到同步任務的執行隊列中。 回到前面的代碼中,咱們接着往下看,

Response result = getResponseWithInterceptorChain();
複製代碼

原來真正獲得response對象是在這裏,經過getResponseWithInterceptorChain();方法獲得的,咱們進入到這個方法中看下是如何執行的。

Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());
    interceptors.add(retryAndFollowUpInterceptor);
    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);
  }
複製代碼

return語句以前的代碼都是建立攔截器鏈的操做,攔截器鏈建立完成後,經過攔截器鏈的proceed方法獲得response對象,咱們繼續跟進

@Override public Response proceed(Request request) throws IOException {
    return proceed(request, streamAllocation, httpCodec, connection);
  }
複製代碼

咱們能夠看到裏面又調用了另外一個重載的proceed方法,咱們跳過去看下,

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

    calls++;
    ···//省略部分代碼
    // Call the next interceptor in the chain.
    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;
  }
複製代碼

咱們能夠看到中間的代碼,這部分代碼就是獲得攔截器鏈中的下一個攔截器,而後進行該攔截器的intercept(next)方法操做,在這個方法中,會繼續調用攔截器鏈中的下一個攔截器的intercept(next)方法調用,最後執行到CallServerInterceptor攔截器,從而獲取真正的response對象,而後一層層的返回,每返回一層,在當前層的攔截器中進行包裝處理,最後返回到這裏,再進行返回,從而獲得咱們要的response對象。攔截器鏈中的詳細執行流程咱們後面再詳細講解。

經過上面的代碼分析,咱們就已經分析完了同步請求的執行流程。下面咱們繼續看下異步請求流程的源碼分析。 ####OkHttp異步請求流程和源碼分析 咱們先來看下如何來執行異步請求

OkHttpClient client = new OkHttpClient.Builder().readTimeout(5, TimeUnit.SECONDS).build();

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

        Call call = client.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的enqueue方法。上面咱們講過,call對象的真正實現是RealCall,那咱們就看RealCall中enqueue方法的實現

@Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }
複製代碼

最後一行,仍是OkHttpClient對象獲取了任務調度器來執行enqueue方法,並將咱們傳遞進來的Callback對象封裝成AsyncCall對象,咱們看下Dispatcher中的enqueue方法:

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

能夠看到,首先進行判斷,若是正在運行的異步任務數量小於指定的數量(64),就將AsyncCall對象添加到正在運行的異步任務隊列中,並加入線程池中執行任務,不然就加入到等待運行的異步任務隊列中去。若是線程池中線程開始運行了,線程池中的執行對象確定是一個線程,那就確定要有run方法的,那麼咱們就來看下AsyncCall究竟是個什麼東東。

final class AsyncCall extends NamedRunnable {
    private final Callback responseCallback;

    AsyncCall(Callback responseCallback) {
      super("OkHttp %s", redactedUrl());
      this.responseCallback = responseCallback;
    }
	······//省略部分代碼
  }
複製代碼

上面代碼中咱們能夠看到,AsyncCall繼承了NamedRunnable,咱們繼續看NamedRunnable是什麼,

public abstract class NamedRunnable implements Runnable {
  protected final String name;

  public NamedRunnable(String format, Object... args) {
    this.name = Util.format(format, args);
  }

  @Override public final void run() {
    String oldName = Thread.currentThread().getName();
    Thread.currentThread().setName(name);
    try {
      execute();
    } finally {
      Thread.currentThread().setName(oldName);
    }
  }

  protected abstract void execute();
}
複製代碼

能夠看到,原來NamedRunnable實現了Runnable接口,確定要執行run方法,那就看下NamedRunnable的run方法,其中最重要的就是execute();這一句了,咱們看下execute()方法,咦,是個抽象方法,這個方法是給其子類來實現的,那麼咱們看下剛剛實現了NamedRunnable類的子類AsyncCall中的executed方法,

@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);
      }
    }
複製代碼

咱們能夠看到熟悉的代碼,熟悉的味道,······,額,咱們看下這一行

Response response = getResponseWithInterceptorChain();
複製代碼

是否是似曾相識呢,對的,在前面的同步執行流程的源碼分析中咱們已經見過這個代碼了,若是你不記得了,就往前面翻一翻,這裏就再也不講解啦。經過這一句,咱們獲得了咱們想要的response對象,那麼咱們看下下面的代碼是作什麼的 try···catch代碼塊中是判斷和對callback方法的調用,咱們忽略它,來看下finally中的代碼,咱們知道,finally中的代碼是必定會執行到的,咱們看下finished方法

void finished(AsyncCall call) {
    finished(runningAsyncCalls, call, true);
  }
  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();
    }
  }
複製代碼

finished方法調用了重載的另外一個finished方法,咱們看下,在finished方法中,首先將剛剛執行完的call對象進行移除隊列,而後咱們看這行代碼

if (promoteCalls) promoteCalls();
複製代碼

咱們看下promoteCalls()方法

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.
    }
  }
複製代碼

頭兩行代碼是判斷正在執行的任務隊列中的任務數量若是大於或等於指定的最大任務數就返回,或者正在等待的任務數爲0也返回,咱們來看下for循環中的代碼,這裏是對等待的任務隊列進行遍歷,首先取出一個任務,判斷是否符合運行條件,若是符合,就將其從等待隊列中移除,添加到運行隊列中去,並加入線程池執行,最後一行,若是正在運行的任務數量大於等於最大指定任務數,就跳出for循環,咱們能夠看到,這個promoteCalls方法的做用就是判斷正在運行的任務數量,若是數量小於指定的任務數,就從等待的任務隊列中取出,添加到正在運行的任務隊列中,並加入線程池中運行,那麼這裏就又能夠回到前面分析過的代碼,那咱們就繼續回到前面的finished方法中去,繼續往下看,

runningCallsCount = runningCallsCount();
複製代碼

咱們來看下這行代碼

public synchronized int runningCallsCount() {
    return runningAsyncCalls.size() + runningSyncCalls.size();
  }
複製代碼

很簡單,只是從新計算正在運行的任務數量,如今咱們回過頭來繼續看finished方法中的最後一部分代碼

if (runningCallsCount == 0 && idleCallback != null) {
      idleCallback.run();
    }
複製代碼

這裏是判斷若是正在執行的任務數量爲0,而且空閒回收線程不爲null,就執行空閒回收線程的run方法進行回收操做。

好了,到此爲止,咱們就已經分析完了同步和異步執行流程,下一篇咱們來分析okhttp的任務調度核心類Dispatcher

相關文章
相關標籤/搜索