okHttp 源码分析

  1. okHttp 源码解析
    基本用列
    (1)首先创建一个 OkHttpClient 对象,那我们看看在构造器中做了什么:
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; // cookie
this.cache = builder.cache; // 缓存
this.internalCache = builder.internalCache;
this.socketFactory = builder.socketFactory;

boolean isTLS = false;
for (ConnectionSpec spec : connectionSpecs) {
  isTLS = isTLS || spec.isTls();
}

if (builder.sslSocketFactory != null || !isTLS) {
  this.sslSocketFactory = builder.sslSocketFactory;
  this.certificateChainCleaner = builder.certificateChainCleaner;
} else {
  X509TrustManager trustManager = systemDefaultTrustManager();
  this.sslSocketFactory = systemDefaultSslSocketFactory(trustManager);
  this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
}

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

如果你想自定义 OkHttpClient 配置的话,就要 new 一个 OkHttpClient.Builder来配置自己的参数了。
之后再调用newCall方法

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

在方法里面其实是创建了一个 RealCall 的对象,那么我们就进入 RealCall 中去看看吧。

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

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

在RealCall构造中其实没做啥操作,就是一些变量赋值,下面接着看RealCall.execute()同步方法

  @Override public Response execute() throws IOException {
    synchronized (this) {
      // 如果该 call 已经被执行过了,就设置 executed 为 true
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    try {
       // 加入 runningSyncCalls 队列中
      client.dispatcher().executed(this);
       // 得到响应 result
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } catch (IOException e) {
      eventListener.callFailed(this, e);
      throw e;
    } finally {
      // 从 runningSyncCalls 队列中移除
      client.dispatcher().finished(this);
    }
  }

execute() 方法为执行该 RealCall,在方法里面一开始检查了该 call 时候被执行。
然后又加入了 Dispatcher 的 runningSyncCalls 中。runningSyncCalls 队列只是用来记录正在同步请求中的 call ,在 call 完成请求后又会从 runningSyncCalls 中移除。

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

看看finish方法

  /** Used by {@code Call#execute} to signal completion. */
  void finished(RealCall call) {
    finished(runningSyncCalls, call, false);
  }

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

可以看出如果是同步方法promoteCalls这个参数是false,从而直接执行回调。后面来分析异步方法为true的时候

下面我们看看Dispatcher->任务调度 构造方法的源码

  //最大并发请求数
  private int maxRequests = 64;
  //每个主机的最大请求数
  private int maxRequestsPerHost = 5;

  /** 线程池 */
  private @Nullable ExecutorService executorService;
  
  /** 将要运行的异步请求队列 */
  private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();

  /** 正在运行的异步请求队列. */
  private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();

  /** 正在运行的同步请求队列. */
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

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

  public Dispatcher() {
  }

  public synchronized ExecutorService executorService() {
    if (executorService == null) {
      executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
          new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
    }
    return executorService;
  }

可见,在同步请求中 Dispatcher 参与的部分很少。但是在异步请求中, Dispatcher 可谓是大展身手。
现在来看看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));
  }

上面的操作其实跟同步都差不多,只有 client.dispatcher().enqueue()不同下面我们去看看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并且正在运行的请求主机数小于5时,把请求加载到runningAsynCalls中并在线程池中执行,否则加入到readyAsynCalls中进行缓存等待。线程池中传进来的参数是AsyncCall,它是ReallCall的内部类,内部类也实现了enqueue方法

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

接下来看看异步方法中的finished(),传的是true

  void finished(AsyncCall call) {
    finished(runningAsyncCalls, call, true);
  }

  private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
      ...
    synchronized (this) {
      if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
      if (promoteCalls) promoteCalls();
      runningCallsCount = runningCallsCount();
      idleCallback = this.idleCallback;
    }
  }

finished方法将此次请求从runningAysncCalls移除后还执行了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.
    }
  }

最关键的一点就是会从readyAsyncCalls中取出下一个请求,加入到runningAsyncCalls中并交由线程池处理。好了,同步和异步的都分析了。现在回到execute方法中的getResponseWithInterceptorChain().
也是我们execute中最重要的方法,我们可以看到这方法是直接返回 Response 对象的,所以,在这个方法中一定做了很多很多的事情。

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.readTimeoutMillis());
    // 利用 chain 来链式调用拦截器,最后的返回结果就是 Response 对象
    return chain.proceed(originalRequest);
}

我们都知道,拦截器是 OkHttp 的精髓。
client.interceptors() ,首先加入 interceptors 的是用户自定义的拦截器,比如修改请求头的拦截器等;
RetryAndFollowUpInterceptor 是用来重试和重定向的拦截器,在下面我们会讲到;
BridgeInterceptor 是用来将用户友好的请求转化为向服务器的请求,之后又把服务器的响应转化为对用户友好的响应;
CacheInterceptor 是缓存拦截器,若存在缓存并且可用就直接返回该缓存,否则会向服务器请求;
ConnectInterceptor 用来建立连接的拦截器;
client.networkInterceptors() 加入用户自定义的 networkInterceptors ;
CallServerInterceptor 是真正向服务器发出请求且得到响应的拦截器;

最后在聚合了这些拦截器后,利用 RealInterceptorChain 来链式调用这些拦截器,利用的就是责任链模式。
RealInterceptorChain 可以说是真正把这些拦截器串起来的一个角色。一个个拦截器就像一颗颗珠子,而 RealInterceptorChain 就是把这些珠子串连起来的那根绳子。
进入 RealInterceptorChain ,主要是 proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec, RealConnection connection) 这个方法:

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

     // 得到下一次对应的 RealInterceptorChain
    RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
        connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
        writeTimeout);
    // 当前次数的 interceptor
    Interceptor interceptor = interceptors.get(index);
   // 进行拦截处理,并且在 interceptor 链式调用 next 的 proceed 方法
    Response response = interceptor.intercept(next);

   // 确认下一次的 interceptor 调用过 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;
  }

对于把 Request 变成 Response 这件事来说,每个 Interceptor 都可能完成这件事,所以我们循着链条让每个 Interceptor 自行决定能否完成任务以及怎么完成任务(自力更生或者交给下一个 Interceptor)。这样一来,完成网络请求这件事就彻底从 RealCall 类中剥离了出来,简化了各自的责任和逻辑。两个字:优雅!

下面分析下ConnectInterceptor,一般只看intercept方法

@Override
public Response intercept(Chain chain) throws IOException {
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    Request request = realChain.request();
    StreamAllocation streamAllocation = realChain.streamAllocation();

    // We need the network to satisfy this request. Possibly for validating a conditional GET.
    boolean doExtensiveHealthChecks = !request.method().equals("GET");
    // 创建 httpCodec (抽象类),分别对应着 http1.1 和 http 2
    HttpCodec httpCodec = streamAllocation.newStream(client, doExtensiveHealthChecks);
    RealConnection connection = streamAllocation.connection();

    return realChain.proceed(request, streamAllocation, httpCodec, connection);
}

这里调用了 streamAllocation.newStream 创建了一个 HttpCodec 的对象。

而 HttpCodec 是一个抽象类,其实现类分别是 Http1Codec 和 Http2Codec 。相对应的就是 HTTP/1.1 和 HTTP/2.0 。
我们来看下 streamAllocation.newStream 的代码:

public HttpCodec newStream(OkHttpClient client, boolean doExtensiveHealthChecks) {
    int connectTimeout = client.connectTimeoutMillis();
    int readTimeout = client.readTimeoutMillis();
    int writeTimeout = client.writeTimeoutMillis();
    boolean connectionRetryEnabled = client.retryOnConnectionFailure();

    try {
        // 在连接池中找到一个可用的连接,然后创建出 HttpCodec 对象
        RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
                writeTimeout, connectionRetryEnabled, doExtensiveHealthChecks);
        HttpCodec resultCodec = resultConnection.newCodec(client, this);

        synchronized (connectionPool) {
            codec = resultCodec;
            return resultCodec;
        }
    } catch (IOException e) {
        throw new RouteException(e);
    }
}

在 newStream(OkHttpClient client, boolean doExtensiveHealthChecks) 中先在连接池中找到可用的连接 resultConnection ,再结合 sink 和 source 创建出 HttpCodec 的对象。

到这里,我们也完全明白了 OkHttp 中的分层思想,每一个 interceptor 只处理自己的事,而剩余的就交给其他的 interceptor 。这种思想可以简化一些繁琐复杂的流程,从而达到逻辑清晰、互不干扰的效果。

End
基本上 OkHttp 的请求响应的流程就讲完了,篇幅有点长长长啊。
不过还有很多点没有涉及到的,比如连接池、缓存策略等等,都是值得我们去深究的。也是需要花很大的功夫才能了解透彻.有讲的不对的地方还请指明!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,290评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,399评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,021评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,034评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,412评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,651评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,902评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,605评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,339评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,586评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,076评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,400评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,060评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,851评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,685评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,595评论 2 270

推荐阅读更多精彩内容