OkHttp的简要分析

OkHttp的功能

OkHttp是Android以及Jdk中封装了一套关于网络协议的库。主要实现了网络相关的功能:

  • 支持Http1.0以及Http2.0
  • 支持WebSocket
  • 支持RESTFul的网络API
  • 实现Post、Get、Delete等请求
  • 内部处理重试以及重定向
  • 允许增加请求缓存

OkHttp的使用

OkHttp的使用也非常简单。

  • 通过OkHttpClient.Builder设置DnsCacheProxy等等
  • 通过Request.Builder生成请求的相关数据
  • 通过OkHttpClient.newCall生成一个Http的请求
  • 通过enqueue或者excute开始请求,在Callback中接收请求参数
// 配置OkHttpClient
OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
clientBuilder.addInterceptor(...);
// 生成Post请求的请求参数
FormBody formBody = new FormBody.Builder()
                .add("username", "admin")
                .add("password", "admin")
                .build();
// 生成Http请求实体
final Request request = new Request.Builder()
                .url("http://www.jianshu.com/")
                .post(formBody)
                .build();
Call call = client.build().newCall(request);
// 加入请求队列中
call.enqueue(new Callback=>...);

OkHttp的设计思想

从下图可以看到OkHttp的设计核心思想 - Chain of Responsibility

  • 应用发送RequestRequest会经过Application Interceptor处理后,交付给OkHttp Core
  • OkHttp Core接收请求后,会从Cache中获取相应的Response
  • 如果没有找到的话,则会将Request经过Network Interceptor处理过后,发送给Server
  • 在接收到Response之后,也会经过Network Interceptor以及Application Interceptor处理后返回应用

OkHttp设计思想

OkHttp的源码分析

getResponseWithInterceptorChain

RealCall中,通过getResponseWithInterceptorChain会完成以下事情:

  • RequestInterceptor的处理
  • 创建Socket连接,发送Http请求报文
  • 接收响应报文,处理请求失败的重试,以及302等重定向
  • 处理缓存策略

而这一切,都在interceptors以及RealInterceptorChain中进行的处理。

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

forWebSocket字段标识当前OkHttpClient是否使用的WebSocket来进行的请求

RealInterceptorChain.proceed

RealInterceptorChain.proceed中,开始按照interceptors的顺序开始责任链处理:

  • 首先处理用户自定义的interceptors
  • 接着再处理RetryAndFollowUpInterceptor
  • 再处理BridgeInterceptor以及CacheInterceptor
  • 再处理ConnectInterceptorCallServerInterceptor
// 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);

interceptors按照顺序开始执行是很重要的,因为有一些变量会在之前的责任链interceptor中被初始化,所以顺序很重要

RetryAndFollowUpInterceptor.intercept

在该函数中,主要完成了:

  • 创建StreamAllocation
    • OkHttpClient中保存Http的连接池connectionPool,以便后续请求复用连接
    • 构建Address对象,传入SSL、Dns等相关信息
    • followUpRequest中根据Response Code来判断是否是需要重定向
@Override public Response intercept(Chain chain) throws IOException {
    ...
    // 创建StreamAllocation为ConnectionInterceptor服务
    StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
        createAddress(request.url()), call, eventListener, callStackTrace);
    this.streamAllocation = streamAllocation;

    while (true) {
       ...
      try {
        // 将请求交付后面的Interceptor进行处理,等待结果
        response = realChain.proceed(request, streamAllocation, null, null);
        releaseConnection = false;
      }catch(Exception e){ ... }
      } finally { ... }
      ...
      Request followUp;
      try { 
        // 处理重定向的结果
        followUp = followUpRequest(response, streamAllocation.route());
      } catch (IOException e) {
        streamAllocation.release();
        throw e;
      }
      ...
  }

realChain.proceed之前,都是对Request进行的处理,而在该函数之后,都是对Response进行的处理。即,一个Interceptor里面就包含了Request以及Response的处理。

ConnectInterceptor.intercept

中间还有BridgeInterceptor以及CacheInterceptor就不说了,它们只是对Cookie以及缓存的判断与处理。与真正的链接无关。

这个Interceptor中主要完成:

  • 通过streamAllocation.newStream创建HttpCodec,可能是Http1Codec,也可能是Http2Codec分别对应Http1.0和Http2.0的请求
@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);
  }
StreamAllocation.newStream

该函数主要会完成这几件事:

  • ConnectionPool中根据Address获取对应的Connection
  • 调用RealConnection.connect开始连接
    • 调用connectSocket,创建Socket
    • 调用Socket.connect开始连接
  • 调用establishProtocol开始建立链接
    • 如果不是Https的话,则判断是否是Http2.0,如果是,则调用startHttp2创建http2ConnectionHttp2.0的连接,并且将协议赋值为Protocol.H2_PRIOR_KNOWLEDGE
    • 如果不是Http2.0话,直接使用rawSocket发送数据,并且将协议赋值为Protocol.HTTP_1_1
    • 如果是Https的话,则会通过connectTls来建立SSL链接,包括SSL握手,证书验证等等
  • 最后调用resultConnection.newCodec,判断http2connection是否为空,来生成Http1Codec还是Http2Codec

CallServerInterceptor.intercept

在之前建立完连接后,在这个Interceptor中,会开始发送数据,并且接收Response。

@Override public Response intercept(Chain chain) throws IOException {

    HttpCodec httpCodec = realChain.httpStream();
    Request request = realChain.request();
    ...
    // 通过Codec写请求头
    httpCodec.writeRequestHeaders(request);

    Response.Builder responseBuilder = null;
    // 接收Response头
    responseBuilder = httpCodec.readResponseHeaders(true);

        long contentLength = request.body().contentLength();
        CountingSink requestBodyOut =
            new CountingSink(httpCodec.createRequestBody(request, contentLength));
        BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
      // 写Request的Body
        request.body().writeTo(bufferedRequestBody);
        bufferedRequestBody.close();
     // 写入请求结束符
    httpCodec.finishRequest();

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

    int code = response.code();
    if (code == 100) {
      // server sent a 100-continue even though we did not request one.
      // try again to read the actual response
      // 读取Response头部数据
      responseBuilder = httpCodec.readResponseHeaders(false);
      response = responseBuilder
              .request(request)
              .handshake(streamAllocation.connection().handshake())
              .sentRequestAtMillis(sentRequestMillis)
              .receivedResponseAtMillis(System.currentTimeMillis())
              .build();
      code = response.code();
    }
    ...
    if (forWebSocket && code == 101) {
      // Connection is upgrading, but we need to ensure interceptors see a non-null response body.
      response = response.newBuilder()
          .body(Util.EMPTY_RESPONSE)
          .build();
    } else {
      // 读取Response Body
      response = response.newBuilder()
          .body(httpCodec.openResponseBody(response))
          .build();
    }
    ...
    return response;
  }

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券