美文网首页
OkHttp源码简析

OkHttp源码简析

作者: Winter_F | 来源:发表于2018-03-26 17:31 被阅读19次

    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的源码学习。

    相关文章

      网友评论

          本文标题:OkHttp源码简析

          本文链接:https://www.haomeiwen.com/subject/reqqcftx.html