OkHttp源碼解析


image.png

序言

上一篇文章介紹了Retrofit的原理逗抑,今天,我們就來(lái)探究一下OkHttp的原理。Retrofit是對(duì)OkHttp做封裝邮府,真正發(fā)送網(wǎng)絡(luò)請(qǐng)求的就是OkHttp框架浙于。
使用Retrofit框架過(guò)程中,對(duì)OkHttp最直觀的認(rèn)識(shí)就是OkHttpClient挟纱,這篇文章會(huì)按如下目錄展開(kāi)介紹:

  • OkHttp的用法
  • OkHttp在Retrofit中的使用
  • OkHttp的原理
  • 對(duì)比OkHttp與其他網(wǎng)絡(luò)庫(kù)
  • 總結(jié)

OkHttp的用法

本文基于OkHttp3.10.0版本進(jìn)行介紹羞酗。

  1. 首先添加OkHttp依賴
implementation("com.squareup.okhttp3:okhttp:3.10.0")

如果項(xiàng)目中添加了Retrofit2依賴,就不用再添加OkHttp3的依賴紊服,因?yàn)镽etrofit2庫(kù)使用到了OkHttp3檀轨,并且已經(jīng)添加了OkHttp3的依賴,所以不用我們重復(fù)添加欺嗤。

  1. 使用方法
//創(chuàng)建OkHttpClient對(duì)象
OkHttpClient client = new OkHttpClient();

String run(String url) throws IOException {
   //創(chuàng)建Request請(qǐng)求對(duì)象
  Request request = new Request.Builder()
      .url(url)
      .build();

   //創(chuàng)建Call對(duì)象参萄,并執(zhí)行同步獲取網(wǎng)絡(luò)數(shù)據(jù)
  Response response = client.newCall(request).execute();
  return response.body().string();
}

總結(jié)一下,OkHttp的使用步驟如下:

  • 創(chuàng)建OkHttpClient對(duì)象
  • 創(chuàng)建Request對(duì)象
  • 創(chuàng)建Call對(duì)象
  • 通過(guò)Call對(duì)象發(fā)起請(qǐng)求煎饼,同步請(qǐng)求調(diào)用call.execute方法讹挎,異步請(qǐng)求調(diào)用call.enqueue方法

OkHttp在Retrofit中的使用

結(jié)合之前的一篇文章Retrofit源碼解析,我們來(lái)看一下在Retrofit中怎樣使用OkHttp吆玖。

  1. 創(chuàng)建OkHttpClient對(duì)象
    在初始化Retrofit對(duì)象的時(shí)候筒溃,我們會(huì)傳入一個(gè)OkHttpClient對(duì)象:
DeviceRetrofit() {
    OkHttpClient okHttpClient = new OkHttpClient.Builder()
            .connectTimeout(Config.HTTP_TIMEOUT, TimeUnit.MILLISECONDS)
            .retryOnConnectionFailure(true)
            .addInterceptor(new DeviceInterceptor())
            .addInterceptor(OkHttpUtils.getHttpInterceptor(TAG))
            .build();

    Retrofit retrofit = new Retrofit.Builder()
            .client(okHttpClient)
            .baseUrl(Config.DEVICE_HOST)
            .addConverterFactory(JacksonConverterFactory.create())
            .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
            .build();
    mService = retrofit.create(DeviceService.class);
}

這里完成了第一步,創(chuàng)建OkHttpClient對(duì)象沾乘。

  1. 創(chuàng)建Request對(duì)象
    當(dāng)我們通過(guò)Retrofit發(fā)起一個(gè)請(qǐng)求的時(shí)候怜奖,在ServiceMethod中會(huì)創(chuàng)建一個(gè)okhttp3.Request對(duì)象:
/**
 * 創(chuàng)建Call對(duì)象
 */
private okhttp3.Call createRawCall() throws IOException {
  Request request = serviceMethod.toRequest(args);
  okhttp3.Call call = serviceMethod.callFactory.newCall(request);
  if (call == null) {
    throw new NullPointerException("Call.Factory returned null.");
  }
  return call;
}

/**
 * 創(chuàng)建Request對(duì)象
 */
Request toRequest(Object... args) throws IOException {
  RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
      contentType, hasBody, isFormEncoded, isMultipart);

  @SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
  ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;

  int argumentCount = args != null ? args.length : 0;
  if (argumentCount != handlers.length) {
    throw new IllegalArgumentException("Argument count (" + argumentCount
        + ") doesn't match expected count (" + handlers.length + ")");
  }

  for (int p = 0; p < argumentCount; p++) {
    handlers[p].apply(requestBuilder, args[p]);
  }

  return requestBuilder.build();
}

這里完成了第二步,創(chuàng)建了一個(gè)Request對(duì)象翅阵。

  1. 創(chuàng)建Call對(duì)象
    第二步的代碼中歪玲,有一個(gè)createRawCall方法,它會(huì)從callFactory工廠中創(chuàng)建一個(gè)Call對(duì)象掷匠,之前說(shuō)過(guò)滥崩,callFactory就是OkHttpClient,所以看OkHttpClient的newCall方法:
@Override public Call newCall(Request request) {
  return RealCall.newRealCall(this, request, false /* for web socket */);
}

這里完成了第三步讹语,創(chuàng)建了一個(gè)RealCall對(duì)象钙皮。

  1. 調(diào)用Call的execute方法
    在RxJavaCallAdapterFactory.CallOnSubscribe.call方法中,會(huì)調(diào)用call.execute()發(fā)送同步請(qǐng)求:
static final class CallOnSubscribe<T> implements Observable.OnSubscribe<Response<T>> {
  private final Call<T> originalCall;

  CallOnSubscribe(Call<T> originalCall) {
    this.originalCall = originalCall;
  }

  @Override public void call(final Subscriber<? super Response<T>> subscriber) {
    // Since Call is a one-shot type, clone it for each new subscriber.
    final Call<T> call = originalCall.clone();

    // Attempt to cancel the call if it is still in-flight on unsubscription.
    subscriber.add(Subscriptions.create(new Action0() {
      @Override public void call() {
        call.cancel();
      }
    }));

    try {
      Response<T> response = call.execute();
      if (!subscriber.isUnsubscribed()) {
        subscriber.onNext(response);
      }
    } catch (Throwable t) {
      Exceptions.throwIfFatal(t);
      if (!subscriber.isUnsubscribed()) {
        subscriber.onError(t);
      }
      return;
    }

    if (!subscriber.isUnsubscribed()) {
      subscriber.onCompleted();
    }
  }
}

這里就完成了第四步募强,調(diào)用call.execute方法株灸。

可見(jiàn),Retrofit中確實(shí)是按照OkHttp3的四個(gè)步驟進(jìn)行使用擎值。

OkHttp的原理

上面介紹了OkHttp的使用方法慌烧,以及Retrofit中怎樣使用OkHttp。那么下面我們就來(lái)分析一下OkHttp的原理鸠儿,OkHttp是怎樣發(fā)送Http請(qǐng)求的屹蚊?我們還是按照這四個(gè)步驟進(jìn)行分析厕氨。

  1. 創(chuàng)建OkHttpClient對(duì)象
    我們看一下OkHttpClient的實(shí)現(xiàn):
public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {
  static final List<Protocol> DEFAULT_PROTOCOLS = Util.immutableList(
      Protocol.HTTP_2, Protocol.HTTP_1_1);

  static final List<ConnectionSpec> DEFAULT_CONNECTION_SPECS = Util.immutableList(
      ConnectionSpec.MODERN_TLS, ConnectionSpec.CLEARTEXT);

  final Dispatcher dispatcher; //請(qǐng)求分發(fā)器
  final @Nullable Proxy proxy;
  final List<Protocol> protocols; //支持的協(xié)議,默認(rèn)http1.1和http2
  final List<ConnectionSpec> connectionSpecs;
  final List<Interceptor> interceptors; //用戶設(shè)置的攔截器
  final List<Interceptor> networkInterceptors;
  final EventListener.Factory eventListenerFactory; //請(qǐng)求事件監(jiān)聽(tīng)器工廠
  final ProxySelector proxySelector;
  final CookieJar cookieJar; //可設(shè)置往請(qǐng)求頭中加入cookie信息
  final @Nullable Cache cache; //可設(shè)置是否緩存請(qǐng)求Response策略
  final @Nullable InternalCache internalCache; //OkHttp3.0之后使用cache汹粤,不再使用此成員變量
  final SocketFactory socketFactory;
  final @Nullable SSLSocketFactory sslSocketFactory;
  final @Nullable CertificateChainCleaner certificateChainCleaner;
  final HostnameVerifier hostnameVerifier;
  final CertificatePinner certificatePinner;
  final Authenticator proxyAuthenticator;
  final Authenticator authenticator;
  final ConnectionPool connectionPool;
  final Dns dns;
  final boolean followSslRedirects;
  final boolean followRedirects;
  final boolean retryOnConnectionFailure;
  final int connectTimeout; //默認(rèn)連接超時(shí)時(shí)間10s
  final int readTimeout; //默認(rèn)讀取超時(shí)時(shí)間10s
  final int writeTimeout; //默認(rèn)寫(xiě)入超時(shí)時(shí)間10s
  final int pingInterval; 
    
  //省略其他代碼
}

部分關(guān)鍵的成員標(biāo)了注釋命斧,各位同學(xué)可以看一下。著重關(guān)注一下Dispatcher類嘱兼,它是網(wǎng)絡(luò)請(qǐng)求分發(fā)器国葬,同步請(qǐng)求和異步請(qǐng)求會(huì)做不同的分發(fā)處理,后面會(huì)詳細(xì)介紹該類芹壕。

  1. 創(chuàng)建Request對(duì)象
    看一下Request的實(shí)現(xiàn):
/**
 * An HTTP request. Instances of this class are immutable if their {@link #body} is null or itself
 * immutable.
 */
public final class Request {
  final HttpUrl url;
  final String method;
  final Headers headers;
  final @Nullable RequestBody body;
  final Object tag;

  private volatile CacheControl cacheControl; // Lazily initialized.

  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;
  }
  
  //省略其他代碼
}

從注釋中可以知道汇四,這個(gè)類代表Http請(qǐng)求,其中包含了Http請(qǐng)求所需的信息踢涌。

  • url:請(qǐng)求地址
  • methdo:請(qǐng)求類型通孽,如GET、POST睁壁、DELETE背苦、PATCH、PUT等
  • headers:請(qǐng)求頭信息
  • body:請(qǐng)求體
  • tag:標(biāo)簽

我們?cè)诳匆幌翲eader類的結(jié)構(gòu):

public final class Headers {
  private final String[] namesAndValues;

  Headers(Builder builder) {
    this.namesAndValues = builder.namesAndValues.toArray(new String[builder.namesAndValues.size()]);
  }

  //省略其他代碼
}

可以看到Headers類中通過(guò)一個(gè)字符串?dāng)?shù)組保存頭信息潘明,保存方式如下{key1, value1, key2, value2, ...}

再看一下RequestBody類的結(jié)構(gòu):

public abstract class RequestBody {
  /** Returns the Content-Type header for this body. */
  public abstract @Nullable MediaType contentType();

  /**
   * Returns the number of bytes that will be written to {@code sink} in a call to {@link #writeTo},
   * or -1 if that count is unknown.
   */
  public long contentLength() throws IOException {
    return -1;
  }

  /** Writes the content of this request to {@code sink}. */
  public abstract void writeTo(BufferedSink sink) throws IOException;

  //省略其他代碼
}

RequestBody是一個(gè)抽象類行剂,其中有兩個(gè)抽象方法:

  • writeTo方法:用于把請(qǐng)求體的內(nèi)容寫(xiě)入到sink(發(fā)送請(qǐng)求到服務(wù)器的對(duì)象)
  • contentType方法:標(biāo)志請(qǐng)求體內(nèi)容的類型。

Request相關(guān)內(nèi)容介紹完畢钉疫,接下來(lái)看一下創(chuàng)建Call部分硼讽。

  1. 創(chuàng)建Call對(duì)象
    Call對(duì)象,我們可以理解為一次網(wǎng)絡(luò)請(qǐng)求的封裝牲阁,一個(gè)Call對(duì)象只能被執(zhí)行一次。那么壤躲,是如果保證只能被執(zhí)行一次的特性呢城菊?
    上面提到過(guò),Retrofit會(huì)為一次請(qǐng)求創(chuàng)建一個(gè)RealCall對(duì)象:
final class RealCall implements Call {
  final OkHttpClient client;
  final RetryAndFollowUpInterceptor retryAndFollowUpInterceptor;

  /**
   * There is a cycle between the {@link Call} and {@link EventListener} that makes this awkward.
   * This will be set after we create the call instance then create the event listener instance.
   */
  private EventListener eventListener;

  /** The application's original request unadulterated by redirects or auth headers. */
  final Request originalRequest;
  final boolean forWebSocket;

  // Guarded by this.
  private boolean executed;

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

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

  //省略其他代碼
}

我們可以看到碉克,RealCall中有個(gè)布爾型的變量executed凌唬,標(biāo)志該Call是否已經(jīng)執(zhí)行了。
在execute和enqueue兩個(gè)方法中都會(huì)校驗(yàn)executed漏麦,這就保證了一個(gè)Call只能執(zhí)行一次客税。

  1. 調(diào)用call.execute或call.enqueue
    再看execute和enqueue兩個(gè)方法,都會(huì)調(diào)用Dispatcher對(duì)應(yīng)的方法撕贞。那就看看Dispatcher的實(shí)現(xiàn):
public final class Dispatcher {
  private int maxRequests = 64;
  private int maxRequestsPerHost = 5;
  private @Nullable Runnable idleCallback;

  /** Executes calls. Created lazily. */
  private @Nullable ExecutorService executorService;

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

  public Dispatcher(ExecutorService executorService) {
    this.executorService = executorService;
  }

  public Dispatcher() {
  }

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

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

  //省略其他代碼
}

Dispatcher中有幾個(gè)成員變量更耻,依次介紹一下:

  • maxRequests:表示異步請(qǐng)求支持的最大并發(fā)請(qǐng)求數(shù)
  • maxRequestsPerHost:表示每個(gè)Host支持的最大并發(fā)請(qǐng)求數(shù),Host可以理解為baseUrl
  • idleCallback:分發(fā)器閑置回調(diào)捏膨,即分發(fā)器中沒(méi)有正在執(zhí)行的請(qǐng)求和準(zhǔn)備執(zhí)行的請(qǐng)求秧均,就會(huì)回調(diào)idleCallback.run方法
  • executorService:線程池食侮,用于管理異步請(qǐng)求的線程
  • readyAsyncCalls:處于準(zhǔn)備狀態(tài)的異步請(qǐng)求隊(duì)列
  • runningAsyncCalls:正在執(zhí)行的異步請(qǐng)求隊(duì)列
  • runningSyncCalls:正在執(zhí)行的同步請(qǐng)求隊(duì)列

著重關(guān)注兩個(gè)異步請(qǐng)求隊(duì)列,什么時(shí)候會(huì)進(jìn)入異步準(zhǔn)備隊(duì)列目胡,什么時(shí)候又會(huì)進(jìn)入異步執(zhí)行隊(duì)列呢锯七?

runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost

滿足上面條件,即異步線程數(shù)量已經(jīng)超過(guò)最大請(qǐng)求數(shù)誉己,或者單個(gè)Host的異步請(qǐng)求數(shù)超過(guò)最大請(qǐng)求數(shù)眉尸,就會(huì)進(jìn)入異步準(zhǔn)備隊(duì)列,否則直接進(jìn)入異步執(zhí)行隊(duì)列巨双。

我們?cè)倏碊ispatcher的executed和enqueue方法效五,發(fā)現(xiàn)executed方法很簡(jiǎn)單:

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

這里只是把同步請(qǐng)求加入同步執(zhí)行隊(duì)列中,并沒(méi)有具體的請(qǐng)求執(zhí)行操作炉峰。那么這個(gè)執(zhí)行操作在哪里呢畏妖?
我們看回RealCall的execute方法:

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

在調(diào)用了Dispatcher.executed方法后,會(huì)調(diào)用getResponseWithInterceptorChain()方法疼阔,那么可以肯定戒劫,這個(gè)方法就是真正執(zhí)行請(qǐng)求的地方。
再看一下異步請(qǐng)求婆廊,RealCall的enqueue方法中迅细,調(diào)用Dispatcher.enqueue方法時(shí),會(huì)創(chuàng)建一個(gè)AsyncCall對(duì)象作為參數(shù)傳入淘邻。那我們看一下AsyncCall的實(shí)現(xiàn):

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

在AsyncCall的execute方法中茵典,也會(huì)調(diào)用getResponseWithInterceptorChain()方法。

也就是說(shuō)宾舅,不管是同步還是異步請(qǐng)求统阿,都是通過(guò)getResponseWithInterceptorChain()方法真正執(zhí)行請(qǐng)求的

接下來(lái)筹我,當(dāng)然是看getResponseWithInterceptorChain方法的實(shí)現(xiàn):

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

這個(gè)方法中扶平,先得到一個(gè)interceptors列表,其中包含的元素有:

  1. 用戶設(shè)置的interceptor列表
  2. RetryAndFollowUpInterceptor:錯(cuò)誤重試和重定向攔截器
  3. BridgeInterceptor:把用戶的request轉(zhuǎn)換為Http的request蔬蕊,把Http的response轉(zhuǎn)換為用戶需要response的轉(zhuǎn)換橋攔截器
  4. CacheInterceptor:處理Response緩存的攔截器
  5. ConnectInterceptor:建立服務(wù)器連接的攔截器
  6. 用戶設(shè)置的networkInterceptors列表
  7. CallServerInterceptor:真正向服務(wù)器發(fā)送請(qǐng)求并且得到響應(yīng)的攔截器

然后創(chuàng)建一個(gè)RealInterceptorChain對(duì)象结澄,繼而調(diào)用其proceed方法。
RealInterceptorChain實(shí)現(xiàn)了Interceptor.Chain接口岸夯,先看一下接口定義:

/**
 * Observes, modifies, and potentially short-circuits requests going out and the corresponding
 * responses coming back in. Typically interceptors add, remove, or transform headers on the request
 * or response.
 */
public interface Interceptor {
  Response intercept(Chain chain) throws IOException;

  interface Chain {
    Request request();

    Response proceed(Request request) throws IOException;

    /**
     * Returns the connection the request will be executed on. This is only available in the chains
     * of network interceptors; for application interceptors this is always null.
     */
    @Nullable Connection connection();

    Call call();

    int connectTimeoutMillis();

    Chain withConnectTimeout(int timeout, TimeUnit unit);

    int readTimeoutMillis();

    Chain withReadTimeout(int timeout, TimeUnit unit);

    int writeTimeoutMillis();

    Chain withWriteTimeout(int timeout, TimeUnit unit);
  }
}

再看RealInterceptorChain的定義:

public final class RealInterceptorChain implements Interceptor.Chain {
  private final List<Interceptor> interceptors;
  private final StreamAllocation streamAllocation;
  private final HttpCodec httpCodec;
  private final RealConnection connection;
  private final int index;
  private final Request request;
  private final Call call;
  private final EventListener eventListener;
  private final int connectTimeout;
  private final int readTimeout;
  private final int writeTimeout;
  private int calls;

  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++;

    // 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);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);

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

我們著重看proceed方法麻献,proceed方法我用一張流程圖來(lái)總結(jié):


image.png

通過(guò)這張圖,可以很清晰的看到執(zhí)行的流程:
通過(guò)RealInterceptorChain一層一層調(diào)用各interceptor的intercept方法猜扮,并且在此過(guò)程中可以處理request勉吻,比如往其中添加參數(shù),或者做一些其他操作破镰。一直到CallServerInterceptor餐曼,它是最后一個(gè)攔截器压储,真正用來(lái)執(zhí)行網(wǎng)絡(luò)請(qǐng)求,從服務(wù)器獲取Response源譬。然后把Response一層一層網(wǎng)上傳遞集惋,在此過(guò)程中,可以對(duì)Response做一定的處理踩娘。
這個(gè)過(guò)程用到了責(zé)任鏈模式刮刑,以鏈?zhǔn)秸{(diào)用,每一個(gè)節(jié)點(diǎn)可以執(zhí)行不同的邏輯养渴,各節(jié)點(diǎn)之間解耦雷绢,擴(kuò)展性強(qiáng),可以隨意添加節(jié)點(diǎn)理卑。

想知道怎樣執(zhí)行網(wǎng)絡(luò)請(qǐng)求的同學(xué)翘紊,可以自行研究各個(gè)Interceptor,尤其是CallServerInterceptor藐唠,它是真正執(zhí)行網(wǎng)絡(luò)請(qǐng)求的地方帆疟。主要是調(diào)用了Http1Codec.flushRequest方法,繼而調(diào)用BufferedSink.flush方法宇立,之后的就留給各位同學(xué)自己去看了踪宠。

最后得到的Response對(duì)象交給Retrofit進(jìn)行轉(zhuǎn)換適配,到這里OkHttp的原理分析完畢妈嘹。

對(duì)比OkHttp與其他網(wǎng)絡(luò)庫(kù)

這里主要對(duì)比幾個(gè)常用的網(wǎng)絡(luò)擴(kuò)柳琢,包括android-async-http、volley润脸、OkHttp和Retrofit柬脸。

  1. android-async-http(loopj)
  • 基于HttpClient
  • 自動(dòng)請(qǐng)求重試
  • 持久化cookie,保存在sp中
  • 作者已經(jīng)停止對(duì)項(xiàng)目維護(hù)津函,因此不推薦在項(xiàng)目中使用
  1. volley(Google)
  • 基于HttpURLConnection
  • 封裝圖片框架肖粮,支持圖片加載
  • 與Activity生命周期的聯(lián)動(dòng),Activit結(jié)束時(shí)取消所有的網(wǎng)絡(luò)請(qǐng)求
  • 適合數(shù)據(jù)量小尔苦,頻率高的請(qǐng)求,不適合上傳或下載大文件行施,原因:線程池默認(rèn)大小是4允坚,Request.getBody()返回的是字節(jié)數(shù)組,也就是對(duì)于Post或Put請(qǐng)求蛾号,會(huì)把傳輸?shù)臄?shù)據(jù)一股腦讀到內(nèi)存中稠项,很容易出現(xiàn)oom
  1. OkHttp(Square)
  • 不基于HttpClient和HttpURLConnection,本身可以理解為一個(gè)封裝之后的HttpURLConnection
  • 集各種優(yōu)點(diǎn)與一身
  • Android4.4源碼可以看到HttpURLConnection已經(jīng)替換為OkHttp實(shí)現(xiàn)鲜结,可見(jiàn)其強(qiáng)大
  1. Retrofit(Square)
  • 基于OkHttp
  • RESTful Api設(shè)計(jì)風(fēng)格
  • 通過(guò)注解配置請(qǐng)求
  • 支持同步展运、異步
  • 易與其他框架配合使用活逆,如Rxjava
  • 使用方法較多,原理比較復(fù)雜拗胜,存在一定的門檻
  • 高度解耦蔗候,擴(kuò)展性極好,堪稱代碼設(shè)計(jì)的典范

總結(jié)

相信看到這里埂软,各位同學(xué)對(duì)OkHttp有了一個(gè)深刻的認(rèn)識(shí)锈遥,再結(jié)合Retrofit那篇文章看,就更能前后貫通理解整個(gè)Retrofit+OkHttp的流程勘畔。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末所灸,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子炫七,更是在濱河造成了極大的恐慌爬立,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,729評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件万哪,死亡現(xiàn)場(chǎng)離奇詭異侠驯,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)壤圃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,226評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門陵霉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人伍绳,你說(shuō)我怎么就攤上這事踊挠。” “怎么了冲杀?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,461評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵效床,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我权谁,道長(zhǎng)剩檀,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,135評(píng)論 1 300
  • 正文 為了忘掉前任旺芽,我火速辦了婚禮沪猴,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘采章。我一直安慰自己运嗜,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,130評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布悯舟。 她就那樣靜靜地躺著担租,像睡著了一般。 火紅的嫁衣襯著肌膚如雪抵怎。 梳的紋絲不亂的頭發(fā)上奋救,一...
    開(kāi)封第一講書(shū)人閱讀 52,736評(píng)論 1 312
  • 那天岭参,我揣著相機(jī)與錄音,去河邊找鬼尝艘。 笑死演侯,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的利耍。 我是一名探鬼主播蚌本,決...
    沈念sama閱讀 41,179評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼隘梨!你這毒婦竟也來(lái)了程癌?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 40,124評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤轴猎,失蹤者是張志新(化名)和其女友劉穎嵌莉,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體捻脖,經(jīng)...
    沈念sama閱讀 46,657評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡锐峭,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,723評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了可婶。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片沿癞。...
    茶點(diǎn)故事閱讀 40,872評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖矛渴,靈堂內(nèi)的尸體忽然破棺而出椎扬,到底是詐尸還是另有隱情,我是刑警寧澤具温,帶...
    沈念sama閱讀 36,533評(píng)論 5 351
  • 正文 年R本政府宣布蚕涤,位于F島的核電站,受9級(jí)特大地震影響铣猩,放射性物質(zhì)發(fā)生泄漏揖铜。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,213評(píng)論 3 336
  • 文/蒙蒙 一达皿、第九天 我趴在偏房一處隱蔽的房頂上張望天吓。 院中可真熱鬧,春花似錦峦椰、人聲如沸失仁。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,700評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至控轿,卻和暖如春冤竹,著一層夾襖步出監(jiān)牢的瞬間拂封,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,819評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工鹦蠕, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留冒签,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,304評(píng)論 3 379
  • 正文 我出身青樓钟病,卻偏偏與公主長(zhǎng)得像萧恕,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子肠阱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,876評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容

  • 簡(jiǎn)介 OkHttp 是一款用于 Android 和 Java 的網(wǎng)絡(luò)請(qǐng)求庫(kù)票唆,也是目前 Android 中最火的一個(gè)...
    然則閱讀 1,220評(píng)論 1 39
  • 這段時(shí)間老李的新公司要更換網(wǎng)絡(luò)層,知道現(xiàn)在主流網(wǎng)絡(luò)層的模式是RxJava+Retrofit+OKHttp,所以老李...
    隔壁老李頭閱讀 32,875評(píng)論 51 405
  • 概述 從OkHttp問(wèn)世以來(lái)屹徘,度娘走趋,google上關(guān)于OkHttp的講解說(shuō)明數(shù)不勝數(shù),各種解讀思想不盡相同噪伊,一千個(gè)...
    夜貓少年閱讀 5,743評(píng)論 9 14
  • OkHttp 知識(shí)梳理系列文章 OkHttp 知識(shí)梳理(1) - OkHttp 源碼解析之入門OkHttp 知識(shí)梳...
    澤毛閱讀 3,389評(píng)論 0 11
  • 前言:對(duì)于OkHttp我接觸的時(shí)間其實(shí)不太長(zhǎng)簿煌,一直都是使用Retrofit + OkHttp 來(lái)做網(wǎng)絡(luò)請(qǐng)求的,但是...
    mecury閱讀 41,041評(píng)論 23 178