okhttp同步流程源码分析

从去年开始,项目里面就一直使用grpc来处理网络请求,由于是新接触的,网络上并没有造好的轮子,我就简单的用MVC模式封装了下(想看的可以:https://www.jianshu.com/p/d6ede6e219f0)。由于封装的很简单粗暴,于是就想参考下其他网络请求框架,然后去完善。刚好以前的老同事都说okhttp用起来很爽,之前也用过okhttp,我这爆脾气,我忍它很久了,上去就是扒源码,一层一层扒的那种。

okhttp同步流程源码分析_第1张图片
小场面.jpg

言归正传,我们知道okhttp分同步跟异步的实现,我们先来看看okhttp的同步使用:

       //同步调用
    1   OkHttpClient okHttpClient = new OkHttpClient();
    2   Request request = new Request.Builder().url("http://......").build();
    3   try {
    4      Response response = okHttpClient.newCall(request).execute();
    5      //。。。
    6  } catch (IOException e) {
    7       e.printStackTrace();
    8  }

look,上面就这么几行代码,就完成了一次同步调用,我们先来看下第一行代码,创建了一个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;
    this.cache = builder.cache;

原来如此,当用new OkHttpClient()这种方式创建OkHttpClient对象,会当场创建一个Builder对象,并给OkHttpClient内部的一些成员变量使用默认值。那我们再去看看Builder的构造:

public Builder() {
      dispatcher = new Dispatcher();
      protocols = DEFAULT_PROTOCOLS;
      connectionSpecs = DEFAULT_CONNECTION_SPECS;
      ......还有N多代码
}
 Builder(OkHttpClient okHttpClient) {
      this.dispatcher = okHttpClient.dispatcher;
      this.proxy = okHttpClient.proxy;
      this.protocols = okHttpClient.protocols;
      this.connectionSpecs = okHttpClient.connectionSpecs;
      ......还有N多代码
}

Builder内部有两个构造,刚才new OkHttpClient()内部使用的是 this(new Builder()),默认当你使用这种方式创建OkHttpClient对象时候,同时Builder通过无参构造函数也创建了builder对象。回到OkHttpClient ,this(new Builder())此处调用了OkHttpClient(Builder builder) {}这个构造并且在此处对成员变量进行赋值。

在这里面,通过搞了个Builder静态内部类,估计是为了方便给OkHttpClient成员变量赋值,这么多成员变量不封装个对象来传递,那当赋值的时候,方法里面得加多少形参,想想都可怕。

很好,那我如果要改变内部成员变量的赋值呢,第二种创建OkHttpClient方式出来了,请看下面:

       //生成OkHttpClient对象的另外一种方式
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        //设置链接超时时间5秒
        builder.connectTimeout(5, TimeUnit.SECONDS);
        ......此处省略N行
        builder.build();

很好,第一行代码就是创建OkHttpClient对象,并且给他内部的成员变量赋值,什么超时时间,拦截器等等之类的为后面请求数据做准备。我们来看看第2行代码,关于Request对象的生成,一开始创建了Request静态内部类Builder对象,下面的.url("..")其实就是给bulider对象里面的成员变量赋值,然后通过.build()方法把builder类当做参数创建一个Request对象,看下面代码:

//这是要分析的第2行代码
Request request = new Request.Builder().url("http://......").build();

//.build()方法
public Request build() {
      if (url == null) throw new IllegalStateException("url == null");
      return new Request(this);
    }

对于Request,也是通过静态内部类Builder给成员变量赋值,我们看看它的构造,刚好上面的.build()方法调用了:

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

发起网络请求一般都需要一个url跟若干个参数(method,body等),Request就是包含这些参数的载体。

目前为止发起请求的前奏已经搞完了,是时候进行请求并获取数据,我们来分析下第4行代码:

Response response = okHttpClient.newCall(request).execute();

我们先肢解下上面的代码,Response, newCall。前者一看就知道是网络请求返回的数据,我们来看看newCall(request)方法,上一步分析的request作为该方法的参数,我们点进去瞧瞧:

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

以上我们可以知道newCall()方法的返回值是一个call,看下面源码,其实这个call就是一个接口,RealCall是call的一个实现类,在分析RealCall之前,我们先来看看call接口的定义:

public interface Call extends Cloneable {
  /** Returns the original request that initiated this call. */
  Request request();
  Response execute() throws IOException;
  void enqueue(Callback responseCallback);
  ...还有其他定义
}

call内部只是定义了一些方法,具体的实现在RealCall类里面,还记得第四行的okHttpClient.newCall(request).execute(), 这个execute()在上面定义有,返回的是Response对象,毫无疑问这里面是进行网络请求的实现。

很好,newCall()方法内部,调用了RealCall的newRealCall()方法并且返回了RealCall对象,我们来瞅瞅newRealCall()方法的内部实现:

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

方法内部就三行代码,第一行new了个RealCall对象call,第二行给call 增加了一个eventListener监听,只是对一个Call的状态监听这里我们不care它,继续扒:

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

这。。尼玛全是给RealCall赋值,把之前创建的okhttpClient对象,Request对象都赋值给了RealCall相对应的成员变量。现在我们回到execute()方法,上面是分析了创建RealCall对象,我们接下来来看看execute()。还记得第4行代码吗(我绝对不是拿来凑字数的):

Response response = okHttpClient.newCall(request).execute();

okHttpClient.newCall(request)创建的其实就是call的实现类RealCall的实例,接下来,激动人心的时刻到了,具体的实现就在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);
    }
  }

方法开头就来个同步锁,锁的是当前RealCall对象,锁的好处大家都懂吧,避免线程不安全的现象发生,这里面就是防止对executed字段的值读写不一致。executed = true的时候,标明正在执行,并抛出Already Executed异常。 client.dispatcher().executed(this)这句其实就是把当前RealCall对象放到一个队列看下面:

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

  //Dispatcher.java
  /** Running synchronous calls. Includes canceled calls that haven't finished yet. */
  private final Deque runningSyncCalls = new ArrayDeque<>();

最终获取Resopnse对象的是这一行: Response result = getResponseWithInterceptorChain(),我们来看看getResponseWithInterceptorChain这个方法,我加了些备注:

Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List interceptors = new ArrayList<>();
    //自定义的拦截器
    interceptors.addAll(client.interceptors());
    //请求出错重试拦截
    interceptors.add(retryAndFollowUpInterceptor);
    //为请求(request before)添加请求头,为响应(Response   Before)添加响应头的拦截
    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);
  }

这个方法里面,interceptors列表,request请求体,还有之前okhttpClient对象的部分成员变量,都作为参数new了一个RealInterceptorChain对象,而且这个方法的返回竟然是chain.proceed(originalRequest),我们直接来看看proceed()方法,里面只是贴出了关键代码:

//RealInterceptorChain.java
public final class RealInterceptorChain implements Interceptor.Chain {
  ......
  @Override public Response proceed(Request request) throws IOException {
    return proceed(request, streamAllocation, httpCodec, connection);
  }
  ......
  public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      RealConnection connection) throws IOException {
      ......
    // 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;
}
}

这里面很巧妙的把所有的interceptor都执行了个遍,一开始index默认是0,每次获取一个interceptor,执行index + 1,可以看出,是有顺序的遍历interceptors集合,看上面代码:Response response = interceptor.intercept(next),关键点就在这里,假装我们没有自定义interceptor,那一开始调用的拦截器就是retryAndFollowUpInterceptor,我们来看看它的intercept()方法:

//RetryAndFollowUpInterceptor.java
@Override public Response intercept(Chain chain) throws IOException {
      ...... 
      Response response;
      boolean releaseConnection = true;
      try {
        response = realChain.proceed(request, streamAllocation, null, null);
        releaseConnection = false;
      } catch (RouteException e) {
        ......
      }
}

看到了吧,这个方法里面调用了realChain.proceed(.....)(对于所有拦截器,都存在这样的一句代码),然后又会执行index + 1,直到完全完全结束。由于拦截器太多,提一下关键的ConnectInterceptor,客户端和服务端建立连接部分:

public final class ConnectInterceptor implements Interceptor {
@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 httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
    RealConnection connection = streamAllocation.connection();

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

核心代码:HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks),这里面实现的是获取一个可用的链接:

public HttpCodec newStream(
      OkHttpClient client, Interceptor.Chain chain, boolean doExtensiveHealthChecks) {
......
  RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
          writeTimeout, pingIntervalMillis, connectionRetryEnabled, doExtensiveHealthChecks);
      HttpCodec resultCodec = resultConnection.newCodec(client, chain, this);
......
}

上面代码中,关键在于findHealthyConnection(),内部又调用了findConnection()方法,很长很长。。。这里不分析,后期专门每个拦截器去研究。

上面获取到了连接,真正获取到response数据,是在CallServerInterceptor里面,这是排在最后的一个拦截器,我们再来看看CallServerInterceptor的interceptor()方法:

@Override public Response intercept(Chain chain) throws IOException {
    ......
    //流分配,StreamAllocation会通过ConnectPool获取或者新生成一个RealConnection来得到一个连接到Server的Connection连接
    StreamAllocation streamAllocation = realChain.streamAllocation();
    RealConnection connection = (RealConnection) realChain.connection();
    Request request = realChain.request();
    ......
    if (responseBuilder == null) {
      realChain.eventListener().responseHeadersStart(realChain.call());
      responseBuilder = httpCodec.readResponseHeaders(false);
    }
    Response response = responseBuilder
            .request(request)
            .handshake(streamAllocation.connection().handshake())
            .sentRequestAtMillis(sentRequestMillis)
            .receivedResponseAtMillis(System.currentTimeMillis())
            .build();
    ......
}

可怕,handshake(握手,socket不是经常扯到握手吗)都出来了,对于我们来说的那熟悉的httpURLconnection怎么没有出现。。我找了下资料,原来OkHttp像HttpUrlConnection一样,实现了一个网络连接的过程,OkHttp和HttpUrlConnection是同一级的,用socket实现了网络连接。

篇幅过长,异步的下一篇在分析了。


okhttp同步流程源码分析_第2张图片
image.png

你可能感兴趣的:(okhttp同步流程源码分析)