天天看點

okhttp執行與攔截器

本文使用okhttp3源碼來講解

1、同步和異步的差別

(1)同步方法execute()

okHttpBuilder.build().newCall()會建立一個RealCall對象

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

然後看下RealCall的execute()方法

//RealCall.java
@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); //執行結束後從雙向連結清單移除
  }
}
           

(2)異步方法enqueue

異步方法enqueue也位于RealCall.java中,具體實作如下

//RealCall.java
@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));//異步執行
}
           

建立了一個AsyncCall對象,并将回調接口作為參數。AsyncCall繼承于抽象類NamedRunnable,NamedRunnable又繼承自Runnable接口,是以這裡新開了一個線程去執行execute()方法。

//NamedRunnable.java
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();
}
           
//RealCall.java
final class AsyncCall extends NamedRunnable { //子線程
  private final Callback responseCallback;

  AsyncCall(Callback responseCallback) {
    super("OkHttp %s", redactedUrl());
    this.responseCallback = responseCallback;
  }

  String host() {
    return originalRequest.url().host();
  }

  Request request() {
    return originalRequest;
  }

  RealCall get() {
    return RealCall.this;
  }

  @Override protected void execute() { //繼承自父類,并在子線程run方法中被調用
    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);
    }
  }
}
           

看下client.dispatcher().enqueue做了啥

//Dispatcher.java
private int maxRequests = 64;
private int maxRequestsPerHost = 5;
synchronized void enqueue(AsyncCall call) {
  if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
    runningAsyncCalls.add(call);
    executorService().execute(call);
  } else {
    readyAsyncCalls.add(call);
  }
}
           

enqueue将任務添加到雙端連結清單中,這裡有兩個隊列-任務隊列和等待隊列,當并發任務數大于64或者相同域名數量大于5個時就加入到等待隊列

2、攔截器種類和順序

使用okhttp進行網絡請求時不管是不同請求(execute())還是異步請求(enqueue(Callback responseCallback))都會走到getResponseWithInterceptorChain()方法來添加攔截器。

//RealCall.java
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()); //第5個參數index=0要注意,後面分析會用到

  return chain.proceed(originalRequest);
}
           

 根據代碼可以看到攔截器添加的順序依次為client.interceptors()、retryAndFollowUpInterceptor、BridgeInterceptor、CacheInterceptor、networkInterceptors、CallServerInterceptor,由于是ArrayList容器是以可以保證順序。

看下攔截鍊的proceed方法,可以看到根據index參數以及RealInterceptorChain,每個攔截器都會執行其intercept方法,在intercept方法中會通過執行後續攔截鍊的proceed方法,這樣就逐漸執行了攔截連上的所有攔截器

public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
    RealConnection connection) throws IOException {
  if (index >= interceptors.size()) throw new AssertionError(); //初始時index 是上面傳入的第五個參數0

  calls++;

  // If we already have a stream, confirm that the incoming request will use it.
  if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) {
    throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
        + " must retain the same host and port");
  }

  // If we already have a stream, confirm that this is the only call to chain.proceed().
  if (this.httpCodec != null && calls > 1) {
    throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
        + " must call proceed() exactly once");
  }

  // Call the next interceptor in the chain.
  RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
      connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
      writeTimeout); //從下個攔截器開始的攔截鍊,初始時由于index是0,則index+1等于1,相當于下一個攔截器的index就變為了1,一次類推index逐漸增加
  Interceptor interceptor = interceptors.get(index);//目前攔截器
  Response response = interceptor.intercept(next);//以新的攔截鍊為參數,執行目前攔截器的intercept方法,這樣就一次執行了攔截鍊上的每個攔截器

  // Confirm that the next interceptor made its required call to chain.proceed().
  if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
    throw new IllegalStateException("network interceptor " + interceptor
        + " must call proceed() exactly once");
  }

  // Confirm that the intercepted response isn't null.
  if (response == null) {
    throw new NullPointerException("interceptor " + interceptor + " returned null");
  }

  if (response.body() == null) {
    throw new IllegalStateException(
        "interceptor " + interceptor + " returned a response with no body");
  }

  return response;
}
           

網上盜個圖說下攔截器流程

okhttp執行與攔截器

繼續閱讀