前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >OkHttp的简要分析

OkHttp的简要分析

作者头像
None_Ling
发布2019-05-23 10:58:10
7980
发布2019-05-23 10:58:10
举报
文章被收录于专栏:Android相关Android相关

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中接收请求参数
代码语言:javascript
复制
// 配置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中进行的处理。

代码语言:javascript
复制
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
代码语言:javascript
复制
// 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来判断是否是需要重定向
代码语言:javascript
复制
@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的请求
代码语言:javascript
复制
@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。

代码语言:javascript
复制
@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;
  }
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019.05.22 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • OkHttp的功能
  • OkHttp的使用
  • OkHttp的设计思想
  • OkHttp的源码分析
    • getResponseWithInterceptorChain
      • RealInterceptorChain.proceed
        • RetryAndFollowUpInterceptor.intercept
          • ConnectInterceptor.intercept
            • CallServerInterceptor.intercept
            相关产品与服务
            SSL 证书
            腾讯云 SSL 证书(SSL Certificates)为您提供 SSL 证书的申请、管理、部署等服务,为您提供一站式 HTTPS 解决方案。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档