美文网首页
源码分析 从关键类分析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