美文网首页
源码分析 从关键类分析OkHttp网络访问流程

源码分析 从关键类分析OkHttp网络访问流程

作者: Parallel_Lines | 来源:发表于2018-10-18 20:22 被阅读0次

在OkHttp访问网络的过程中,有三个类充当了重要角色,分别是StreamAllocationRealConnectionHttpCodec。通过分析这三个类在网络访问中扮演的角色,可以加深对OkHttp框架的理解。

本文需要对OkHttp有一定了解:

  1. OkHttp使用Deliver实现线程池调度,本文暂不讨论,只讨论关键网络访问流程。
  2. OkHttp采用拦截器链实现网络请求的顺序访问,拦截器链的知识参考另一篇文章。
  3. OkHttp不同于Volley底层使用HttpUrlConnection,而是使用Socket实现Http请求。不了解Socket如何实现Http可以自行百度。
  4. OkHttp没有使用java.io进行流的操作,而是使用okio。不了解Okio可以自行百度稍加了解。

OkHttp的网络访问过程是通过以下几个拦截器实现的:
RetryAndFollowUpInterceptor
BridgeInterceptor
CacheInterceptor
ConnectInterceptor
CallServerInterceptor

为简单起见,这里只将如下部分拦截器作为网络访问的关键流程:
RetryAndFollowUpInterceptor
ConnectInterceptor
CallServerInterceptor

从简化版可以看出,这是一个带有重试、重定向功能的网络访问。

下面将通过拦截器的流程图分析这三个关键类。

前提知识

在分析流程图之前,需要如下前提知识。

关键类简介

HttpCodec
HttpCodec里维护了BufferedSource和BufferedSink,可以把BufferedSource看作BufferInputStream,BufferedSink看作BufferOutputStream。通过Sink,可以写入Socket实现Http请求中需要的Header和Body;通过Source,可以获取服务器返回的Header和Body。

RealConnection
RealConnection负责Socket连接,并获取HttpCodec需要的Source和Sink。

StreamAllocation
用于获取RealConnection、调用RealConnection连接、初始化HttpCodec。同时它也是OkHttp连接复用池的重要标志位。

连接复用

OkHttp的一个重要功能,就是连接复用。
它复用的就是RealConnection,即一个Socket连接。(Socket连接只需要host、port、scheme,和path无关)这样可以极大的减少建立Socket连接的开销。

这里简要说明下复用的实现原理:

get方法:

  RealConnection get(Address address, StreamAllocation streamAllocation) {
    assert (Thread.holdsLock(this));
    for (RealConnection connection : connections) {
      if (connection.allocations.size() < connection.allocationLimit
          && address.equals(connection.route().address)
          && !connection.noNewStreams) {
        streamAllocation.acquire(connection);
        return connection;
      }
    }
    return null;
  }

每次return前,该connection使用次数的计数器就会+1,下面看一下计数器的源码:

  public void acquire(RealConnection connection) {
    assert (Thread.holdsLock(connectionPool));
    connection.allocations.add(new StreamAllocationReference(this, callStackTrace));
  }

其中,allocations是List<Reference<StreamAllocation>>,即计数器是通过List中add StreamAllocation实现的

connection使用次数计数器仅会在复用连接池get以及该connection刚创建完毕时才会add,这样才能准确的监控每个connection从复用连接池中被获取的次数。同时要记住,StreamAllocation充当了被复用次数的标志。

复用连接池会定期清理长期不用的Connection,源码中会根据List<Reference<StreamAllocation>>中元素数量排序筛选出不满足要求的Connection,并将它关闭,详情可以参考ConnectionPool的cleanup()和pruneAndGetAllocationCount()方法,这里不重点分析了。

流程分析

上面抽出了网络访问的关键步骤(拦截器),以及简介了部分关键类的主要功能。

下面就 关键拦截器 + 关键类 + 复用 机制,给出流程图。

![关键流程图.png](https://img.haomeiwen.com/i6103019/01e58427d4cbb926.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

注意:

RetryAndFollowUpInterceptor中的sameConnection的源码如下:

  private boolean sameConnection(Response response, HttpUrl followUp) {
    HttpUrl url = response.request().url();
    return url.host().equals(followUp.host())
        && url.port() == followUp.port()
        && url.scheme().equals(followUp.scheme());
  }

从流程图可以看出,本次访问与相邻重定向访问假如host、port、scheme均相同,访问会复用上一次的StreamAllocation以及Connection,而不是从复用连接池获取Connection,复用连接池不会计数。

从流程图分析,当重定向序列如下时,会出现下述情况:

访问序列/类对象 hostA/aaa hostA/bbb hostB/kkk hostA/ccc
StreamAllocation sa1 sa1 sa2 sa3
RealConnection rc1 rc1 rc2 rc1
HttpCodec hc1 hc2 hc3 hc4

其中名词可以这样理解:
对于链接https://www.jianshu.com/p/1036a7b68bce
hostA/aaa的hostA为https://www.jianshu.com
aaa为p/1036a7b68bce
sa1表示网络执行过程中的StreamAllocation对象,hostA/aaa和hostA/bbb都是sa1,表示同一对象。

从上述可以看出:

RealConnection对应一个Socket连接,它是被复用连接池保存的长链接,当存在相同域名的访问时,可以被复用。故会出现上述从域名A→域名B→域名A时,Connection被复用的情况。

StreamAllocation可以理解为RealConnection和HttpCodec的调度器,它控制着RealConnection的来源--可能来自上一次访问的connection、或是来自连接池、或是直接初始化一个新的。同时,RealConnection的Socket连接也需要通过StreamAllocation调起,并生成HttpCodec所需要的io交互的变量。

StreamAllocation的另一个功能,就是充当复用连接池的计数器。从序列表可以看出,hostA/aaa→hostA/bbb的重定向过程中,并没有重新new一个StreamAllocation,而是直接复用上一次的(StreamAllocation中持有connection),这样效率很明显比从复用连接池拿connection要高。而且StreamAllocation的创建也与connection使用次数计数一一对应。

HttpCodec在HttpCodec中充当一次网络访问,从序列表也可以看出,每次网络访问,即使是每次重定向,都会创建一个新的HttpCodec,用于传本次请求的参数以及拿到本次请求的返回值。

如有问题还望评论区指出,感谢!

相关文章

网友评论

      本文标题:源码分析 从关键类分析OkHttp网络访问流程

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