OKHttp網絡請求原理流程解析

1. Okhttp基本使用

初始化可以添加自定義的攔截器

OkHttpClient okHttpClient = new OkHttpClient.Builder()
              .connectTimeout(30, TimeUnit.SECONDS)
              .writeTimeout(30, TimeUnit.SECONDS)
              .readTimeout(30, TimeUnit.SECONDS)
              .addInterceptor(interceptorImpl).builder();//創建OKHttpClient的Builder

基本使用

String url = "http://wwww.baidu.com";
final Request request = new Request.Builder()
        .url(url)
        .get()//默認就是GET請求,可以不寫
        .build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
        Log.d(TAG, "onFailure: ");
    }

    @Override
    public void onResponse(Call call, Response response) throws IOException {
        Log.d(TAG, "onResponse: " + response.body().string());
    }
});

一般的使用大致就是這樣的

2.從OkHttpClient創建開始入手分析

OkHttpClient.Builder()使用builder模式,用戶可以自定義相應的參數

開發一般會用到的是

  .connectTimeout(30, TimeUnit.SECONDS)
  .writeTimeout(30, TimeUnit.SECONDS)
  .readTimeout(30, TimeUnit.SECONDS)
  .addInterceptor(interceptorImpl)

連接時間,寫時間,讀時間以及對應的Interceptor相關的攔截器

3.構建Request

Request用的也是Builder模式,好處主要是可以動態配置相應的參數

 Request(Builder builder) {
    this.url = builder.url;
    this.method = builder.method;
    this.headers = builder.headers.build();
    this.body = builder.body;
    this.tags = Util.immutableMap(builder.tags);
  }

tag主要是做標識的,請求返回爲null時候的標識操作

4.構建Call

構建Call,主要是調用RealCall.newRealCall方法,並在其內部添加了一個事件回調監聽

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

而在newRealCall方法中同時也調用了RealCall的構造方法
構造方法中加入了RetryAndFollowUpInterceptor重試攔截器,okhttp中加入了很多攔截器,這也是一大特色

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

5. 執行異步請求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));
  }

executed以及synchronized主要是用來防止重複操作和多線程同步用的

接下來的方法

private void captureCallStackTrace() {
    Object callStackTrace = Platform.get().getStackTraceForCloseable("response.body().close()");
    retryAndFollowUpInterceptor.setCallStackTrace(callStackTrace);
  }

重試監聽器做一些棧StackTrace記錄,以及eventListener.callStart(this);事件監聽做回調處理,不影響流程

接着就到了Dispatcher的enqueue方法

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

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

Dispatcher中定義三個隊列分別是readyAsyncCalls異步等待,同步運行runningAsyncCalls以及runningSyncCalls異步運行隊列,enqueue方法中,當運行異步隊列個數小於最大請求數(64)並且同一Host請求個數小於maxRequestsPerHost(5)則加入異步運行隊列,並且用線程執行,否則加入異步等待隊列中,這是okhttp的線程隊列優化

6.查看AsyncCall的run方法

AsyncCall 繼承了NamedRunnable,其內部會run方法會調用execute(),代碼如下

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

signalledCallback這個標識用來處理是否打印對應的日誌,這裏可以看到Response類,說明網絡請求是在getResponseWithInterceptorChain中完成的,之後會回調當前的Call狀態值

7.真正的網絡請求的getResponseWithInterceptorChain

Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());
    //失敗重試攔截器
    interceptors.add(retryAndFollowUpInterceptor);
    //request和response攔截器
    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);
  }

加入各式各樣的攔截器,各個攔截器之間不耦合,易於用戶的自己配置,最後調用RealInterceptorChain的proceed方法

8.RealInterceptorChain的proceed方法

public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation,
      HttpCodec httpCodec, RealConnection connection, int index, Request request, Call call,
      EventListener eventListener, int connectTimeout, int readTimeout, int writeTimeout) {
    this.interceptors = interceptors;
    this.connection = connection;
    this.streamAllocation = streamAllocation;
    this.httpCodec = httpCodec;
    this.index = index;
    this.request = request;
    this.call = call;
    this.eventListener = eventListener;
    this.connectTimeout = connectTimeout;
    this.readTimeout = readTimeout;
    this.writeTimeout = writeTimeout;
  }
  
  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;
  }

構造方法中加入了eventListener事件監聽,看來okhttp中eventListener的監聽一直延伸到這裏,還加入了

    this.connectTimeout = connectTimeout;
    this.readTimeout = readTimeout;
    this.writeTimeout = writeTimeout;

連接時間的配置

要重點關注的是index這個字段,前面傳進來的時候,默認是0,而在proceed方法中,又重新執行了RealInterceptorChain的構造方法,並通過 interceptors.get(index)獲取下一個攔截器,並且執行interceptor.intercept(next)方法,隨便找一個攔截器看看

public final class BridgeInterceptor implements Interceptor {

  @Override public Response intercept(Chain chain) throws IOException {
    //省略部分代碼
    Response networkResponse = chain.proceed(requestBuilder.build());
    //省略部分代碼
    return responseBuilder.build();
  }
}

攔截器內部又重新調用了chain.proceed的方法,這和遞歸操作類似,也是okHttp最經典的責任鏈模式。

9.同步操作

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

同步請求也是通過getResponseWithInterceptorChain來完成的,流程更簡單

10.大致的流程圖

image

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