OKHttp 3.14.X源碼架構學習(一)

OKHttp 3.14.X源碼架構

如何使用OKHttp

這裏以非Kotlin版本介紹

  • 首先在build.gradle中添加組件:
implementation 'com.squareup.okhttp3:okhttp:3.14.2'
  • 在MainActivity中調用OkHttp的API:
//注意這裏,一般我們用單例,Okhttp官方描述如果不用單例,線程管理等都失效了,浪費開銷
/*Http performs best when you create a single {@code OkHttpClient} instance and reuse it for
 * all of your HTTP calls. This is because each client holds its own connection pool and thread
 * pools. Reusing connections and threads reduces latency and saves memory. Conversely, creating a
 * client for each request wastes resources on idle pools.
 */
OkHttpClient client = new OkHttpClient();

String run(String url) throws IOException {
  Request request = new Request.Builder()
      .url(url)
      .build();

  Response response = client.newCall(request).execute();
  return response.body().string();
}

OKHttp工作流程圖

OkHttp工作流程圖

OKHttpClient的創建

通過okhttp源碼分析,直接創建的 OkHttpClient對象並且默認構造builder對象進行初始化

public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {
  public OkHttpClient() {
       this(new Builder());
  }
  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;
    
    ...
    
    this.hostnameVerifier = builder.hostnameVerifier;
    this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner(
        certificateChainCleaner);
    this.proxyAuthenticator = builder.proxyAuthenticator;
    this.authenticator = builder.authenticator;
    this.connectionPool = builder.connectionPool;
    this.dns = builder.dns;
    this.followSslRedirects = builder.followSslRedirects;
    this.followRedirects = builder.followRedirects;
    this.retryOnConnectionFailure = builder.retryOnConnectionFailure;
    this.connectTimeout = builder.connectTimeout;
    this.readTimeout = builder.readTimeout;
    this.writeTimeout = builder.writeTimeout;
    this.pingInterval = builder.pingInterval;
  }
}

Request的發出

一般如下:

Request request = new Request.Builder().url("url").build();
okHttpClient.newCall(request).enqueue(new Callback() {
  @Override
  public void onFailure(Call call, IOException e) {

 }

 @Override
 public void onResponse(Call call, Response response) throws IOException {

 }
});

初始化構建者模式和請求對象,並且用URL替換Web套接字URL。

public final class Request {
    public Builder() {
      this.method = "GET";
      this.headers = new Headers.Builder();
    }
    public Builder url(String url) {
      ......

      // Silently replace web socket URLs with HTTP URLs.
      if (url.regionMatches(true, 0, "ws:", 0, 3)) {
        url = "http:" + url.substring(3);
      } else if (url.regionMatches(true, 0, "wss:", 0, 4)) {
        url = "https:" + url.substring(4);
      }

      HttpUrl parsed = HttpUrl.parse(url);
      ......
      return url(parsed);
    }
    public Request build() {
      ......
      return new Request(this);
    }
}

主裏主要是調用了newCall方法,我們跟進去看看:

public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {
   @Override 
   public Call newCall(Request request) {
    return new RealCall(this, request, false /* for web socket */);
   }
}

發現去new RealCall了,我們跟到RealCall 看看:

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

可以看到只是初始化一些成員變量,真正的調用是RealCall裏面的enqueue(添加到隊列)/execute(立即執行)方法,我們看看:

  void enqueue(AsyncCall call) {
    synchronized (this) {
       /** Ready async calls in the order they'll be run. */
  	   //private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
  	   // readyAsyncCalls是一個雙端隊列,把該call加入到準備的隊列裏
      readyAsyncCalls.add(call);

      // Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to
      // the same host.
      if (!call.get().forWebSocket) {
        AsyncCall existingCall = findExistingCallWithHost(call.host());
        if (existingCall != null) call.reuseCallsPerHostFrom(existingCall);
      }
    }
    promoteAndExecute();
  }

上面不難發現最終調用了promoteAndExecute(),我們跟進去看看:

  /**
   * Promotes eligible calls from {@link #readyAsyncCalls} to {@link #runningAsyncCalls} and runs
   * them on the executor service. Must not be called with synchronization because executing calls
   * can call into user code.
   *
   * @return true if the dispatcher is currently running calls.
   */
  private boolean promoteAndExecute() {
    assert (!Thread.holdsLock(this));

    List<AsyncCall> executableCalls = new ArrayList<>();
    boolean isRunning;
    synchronized (this) {
      for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
        AsyncCall asyncCall = i.next();

        if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity. // 最大的線程請求的時候,不再添加, maxRequests默認值是:64
        if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; // Host max capacity. // 最大的DNS解析爲:5

        i.remove();
        asyncCall.callsPerHost().incrementAndGet();
        executableCalls.add(asyncCall);
        runningAsyncCalls.add(asyncCall);
      }
      isRunning = runningCallsCount() > 0;
    }
    // 循環執行
    for (int i = 0, size = executableCalls.size(); i < size; i++) {
      AsyncCall asyncCall = executableCalls.get(i);
      asyncCall.executeOn(executorService());
    }

    return isRunning;
  }

有一個問題,enqueue是拿到Reponse的? 它和execute區別?

帶着問題,我們可以在RealCall中可以發現 final class AsyncCall extends NamedRunnable

這個NamedRunnable是什麼?

/**
 * Runnable implementation which always sets its thread name.
 */
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();
}

根據源碼發現它原來是一個runable,並且抽象了execute,所以execute就是enqueue去執行的核心方法,那麼我就可以看到execute方法的實現:

@Override protected void execute() {
      boolean signalledCallback = false;
      transmitter.timeoutEnter();
      try {
        Response response = getResponseWithInterceptorChain();
        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 {
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }

實際上跟execute方法一樣,都是調用了getResponseWithInterceptorChain,跟進去看看:

Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());// 1.添加自定義的攔截器
    interceptors.add(new RetryAndFollowUpInterceptor(client));// 2.重試攔截
    interceptors.add(new BridgeInterceptor(client.cookieJar())); // 3.Build一個適配的Request
    interceptors.add(new CacheInterceptor(client.internalCache()));// 4.添加緩存攔截器
    interceptors.add(new ConnectInterceptor(client)); // 5.ConnectInterceptor驗證Get請求
    if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors()); // 6.非Websocket下創建的自定義網絡攔截器
    }
    interceptors.add(new CallServerInterceptor(forWebSocket)); // 7. 真實的IO訪問,使用了OKIO庫去實現的

    Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0,
        originalRequest, this, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    .....
      return response;
  }

從上面的設計模式可以看出來OKHttp使用了責任鏈的設計模式. 至此流程已經完畢. 下面學習一些優秀的細節.

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