OkHttp源码简析

达芬奇密码2018-08-09 12:57

Android平台有很多优秀的开源库,OkHttp绝对是其中的佼佼者,它是Square出品的一个网络通讯库,功能强大、稳定可靠,以至于Google也在4.4以后用OkHttp来实现HttpURLConnectiond的底层功能,本文将通过阅读项目的源码,一步步构建OkHttp的项目架构,了解这个强大的通讯库是如何设计的。

简单用例

我们先看看OkHttp的基本使用方法 Get请求:

OkHttpClient client = new OkHttpClient();

String run(String url) throws IOException {
  Request request = new Request.Builder()
      .url(url)
      .build();

  Response response = client.newCall(request).execute();
  return response.body().string();
}

Post请求:

public static final MediaType JSON
    = MediaType.parse("application/json; charset=utf-8");

OkHttpClient client = new OkHttpClient();

String post(String url, String json) throws IOException {
  RequestBody body = RequestBody.create(JSON, json);
  Request request = new Request.Builder()
      .url(url)
      .post(body)
      .build();
  Response response = client.newCall(request).execute();
  return response.body().string();
}

发送请求需要首先构建一个担当客户端角色的OkHttpClient,再构建我们要发送的请求Request,填充好url和body后,通过OkHttpClient把Request发送出去,获取并返回Response,这个流程如下图:

OkHttp还支持异步请求,异步请求的使用方式:

client.newCall(request).enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
    }

    @Override
    public void onResponse(Call call, Response response) throws IOException {
        System.out.println(response.body().string());
    }
});

异步请求会通过Callback来监听异步请求结果,加上异步请求的流程图如下:

以上是OkHttp的基本用例,接下来我们要进入到源码来分析它究竟是怎么工作的。

构建OkHttpClient

OkHttpClient可以使用Builder来配置超时、代理、DNS等各种参数,之后调用builder()来生成OkHttpClient对象。

OkHttpClient client = new OkHttpClient.Builder()
    .addNetworkInterceptor(new LoggingInterceptor())
    ...... //各种配置
    .build();

查看源码可以看到,OkHttpClient的无参构造方法实际上也是通过调用Builder方法来构建,只是省略了配置参数的过程。

public OkHttpClient() {
  this(new Builder());
}

之后,Builder的各个配置会保存在Client中,供后面的操作来读取使用。加上Builder的流程图如下:

发送请求

无论是同步还是异步请求,都需要先调用newCall方法获取Call对象,而Call对象是什么呢?

public interface Call extends Cloneable {

  Request request();

  Response execute() throws IOException;

  void enqueue(Callback responseCallback);

  void cancel();

  boolean isExecuted();

  boolean isCanceled();

  Call clone();

  interface Factory {
    Call newCall(Request request);
  }
}

Call是一个已经准备好执行的请求,值得注意的是,它是工厂模式的一个产物,OkHttpClient就是一个继承了Call.Factory接口的工厂类。

public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {

  ...

  @Override public Call newCall(Request request) {
    return RealCall.newRealCall(this, request, false);
  }

  ...

}

在OkHttpClient的newCall方法中,创建的是一个RealCall对象,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先检查自身是否已经执行过,然后执行eventListener的回调并在dispatcher中通知client自己的状态改变,关键的发送请求操作是在getResponseWithInterceptorChain方法中实现的。 再看看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));
  }

final class AsyncCall extends NamedRunnable {

  ...

  @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来实现,OkHttpClient的Dispatcher会在适当的时机调用AsyncCall,在AsyncCall中我们也能看到getResponseWithInterceptorChain的身影,事实上,所有请求操作都是在getResponseWithInterceptorChain中实现的,想要知道请求的发送过程,还是要看getResponseWithInterceptorChain的实现方式。

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

getResponseWithInterceptorChain中生成了一个Interceptor的列表,并根据这个列表维护一个Interceptor链,每一个Interceptor都实现了一部分功能,发送请求就是按顺序执行这个Interceptor链的过程。

  • RetryAndFollowUpInterceptor负责失败重试和重定向请求
  • BridgeInterceptor负责桥接应用和网络间的请求和响应
  • CacheInterceptor负责管理网络缓存
  • ConnectInterceptor负责和服务器建立连接
  • CallServerInterceptor负责发起请求

除此之外,RealCall还会在合适的时机插入开发者定义的ApplicationInterceptors和NetworkInterceptor,两种Interceptor的主要差别在于是否一定会被执行和执行的次数(wiki ),从这两种Interceptor插入时机来看,我们就不难理解他们是怎么实现的。

Interceptor是OkHttp最核心的设计(也是我认为最优雅的设计),它把请求、缓存、桥接等各个功能都解耦成一个个的Interceptor,然后用一条责任链完美地串联在一起。

接下来我们关注发送请求的两个关键步骤:建立连接和发送数据

建立连接

ConnectInterceptor的代码:

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

ConnectInterceptor的职责很简单,就是构造一个HttpCodec并放入Interceptor链中,HttpCodec是一个负责对请求和响应进行编码和解码操作的抽象类,所有IO操作都由它来封装实现。ConnectInterceptor会使用RetryAndFollowUpInterceptor生成的StreamAllocation,找到可用的 RealConnection,根据HTTP版本构造对应的HttpCodec实体对象,提供给后面的Interceptor使用。

发送数据

CallServerInterceptor的代码,省略了部分:

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

  // 发送Request的Header
  httpCodec.writeRequestHeaders(request);
  Response.Builder responseBuilder = null;
  // 判断是否需要发送Body
  if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) 
    if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
      httpCodec.flushRequest();
      // 100-coutinue的处理
      responseBuilder = httpCodec.readResponseHeaders(true);
    }
    if (responseBuilder == null) {
      // 发送Request的Body
      long contentLength = request.body().contentLength();
      CountingSink requestBodyOut =
          new CountingSink(httpCodec.createRequestBody(request, contentLength));
      BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
      request.body().writeTo(bufferedRequestBody);
      bufferedRequestBody.close();
    } else if (!connection.isMultiplexed()) {
      streamAllocation.noNewStreams();
    }
  }
  httpCodec.finishRequest();

  if (responseBuilder == null) {
    // 读取Response的Header
    responseBuilder = httpCodec.readResponseHeaders(false);
  }
  Response response = responseBuilder
      .request(request)
      .handshake(streamAllocation.connection().handshake())
      .sentRequestAtMillis(sentRequestMillis)
      .receivedResponseAtMillis(System.currentTimeMillis())
      .build();
  int code = response.code();
  // 读取Response的Body
  if (forWebSocket && code == 101) {
    response = response.newBuilder()
        .body(Util.EMPTY_RESPONSE)
        .build();
  } else {
    response = response.newBuilder()
        .body(httpCodec.openResponseBody(response))
        .build();
  }
  if ("close".equalsIgnoreCase(response.request().header("Connection"))
      || "close".equalsIgnoreCase(response.header("Connection"))) {
    streamAllocation.noNewStreams();
  }
  return response;
}

CallServerInterceptor使用前面Interceptor生成的产物,一步步发送Request的Header和Body,再接收Response的Header和Body,最后返回构建好的Response。CallServerInterceptor作为Interceptor链的最后一环,最终实现了Http通讯功能。

总结

最后再总结一下OkHttp的流程,首先用OkHttpClient.Builder构建OkHttpClient,通过newCall方法把配置好的Request转换为可执行的RealCall,根据同步或异步使用不同的调度方式,构建Chain按顺序执行每个Interceptor的操作,最终返回Response。

OkHttp的架构简单优雅,拓展性也因为Interceptor的设计而异常强大,和Retrofit结合使用的话还能发挥更强大的功力。这次对OkHttp的源码简析还有很多地方没有仔细说明,比如RetryAndFollowUpInterceptor失败和重定向的操作、CacheInterceptor的缓存方案等等,感兴趣的同学可以下载OkHttp的源码学习。

网易云新用户大礼包:https://www.163yun.com/gift

本文来自网易实践者社区,经作者冯文浩授权发布。