栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

OKHttp源码分析(一)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

OKHttp源码分析(一)

目录

OKHttp的优缺点

OKHttp的主流程

OKHttp核心类分析

OKHttp涉及到的设计模式


OKHttp的优缺点

优缺点是比较而言,对比目前的网络框架

框架名称优点        缺点适用场景
OKHttp

1)Http高性能库,属于底层网络框架

2)内置连接池,支持连接复用

3)通过Gzip压缩响应体数据

4)支持同步阻塞和异步阻塞两种方式

使用时需要进一步封装重量级网络交互场景
Volley1)基于

HttpUrlConnection,是封装后的网络框架。

2)可扩展性好,可支持HttpClient、HttpUrlConnection和Okhttp

不适合大的下载或者流式传输操作,数据方法放到byte[]数组里,消耗内存

适合轻量级网络交互,网络请求频繁,传输数据量小,不适合做文件(音视频)操作(上传/下载)
Retrofit

1)底层基于okhttp,属于封装后的网络框架

2)封装好,效率高,简洁易用

3)支持RxJava

1)扩展性差

2)项目中常常RxJava+Retrofit+Okhttp组合,来搭建网络框架,具有一定学习成本

适合大型项目,重量级网络交互场景,网络请求频繁、传输数据量大

关于OkHttp我们知道:

1)OkHttp基于Socket通信,它更倾向于底层,会对Http协议进行完全的封装

可以简单理解为:Okhttp通过Socket和服务器进行了TCP连接,并将需要的请求信息按照Http协议的格式封装,通过Socket连接发送到服务器,再读取服务器的响应

2)Android 4.4后,HttpURLConnection底层实现就是OkHttp,相当于官方认证

基于此,我们探究OkHttp源码才更有意义,对网络框架的底层原理才会更加了解

OKHttp的主流程

通过上图,我们了解OkHttp的核心流程,具体详细过程可以看下面的文字解读

  1. 通过建造者模式,构建OkHttp的客户端请求发起者OkHttpClient
  2. 通过构建者模式,构建Request的请求体,组装参数,header、method、Url等
  3. 创建Call,真正的请求在RealCall中完成,Dispatcher调度器负责管理RealCall,通过execute()或者enquene()完成同步/异步请求
  4. execute()后者enquene()最终会调用getResponseWithInterceptorChain()方法,从拦截器链条interceptors中获取结果

OKHttp核心类分析

从上面的分析可知,RealCall是Call的具体实现类,我们来看RealCall他的exectue()方法

override fun execute(): Response {
    //判断有没有执行过,没有执行进行置位标记
    check(executed.compareAndSet(false, true)) { "Already Executed" }

    //请求超时开始计时
    timeout.enter()
    callStart()
    try {
      // 第一步 交给dispatcher开始请求
      client.dispatcher.executed(this)
      //第二步 获取请求结果
      return getResponseWithInterceptorChain()
    } finally {
      //第三步 结束请求任务
      client.dispatcher.finished(this)
    }
}

关键代码已经做了注释,可以看到,整个请求核心步骤分三步完成

第一步:是dispatcher的execute方法,我们进入Dispatcher类来看下,Dispatcher从字面意思来看,就是调度器,主要负责管理网络请求线程,异步请求通过线程池ExecutorService来管理

@get:Synchronized var maxRequests = 64
    set(maxRequests) {
      require(maxRequests >= 1) { "max < 1: $maxRequests" }
      synchronized(this) {
        field = maxRequests
      }
      promoteAndExecute()
    }

@get:Synchronized var maxRequestsPerHost = 5
    set(maxRequestsPerHost) {
      require(maxRequestsPerHost >= 1) { "max < 1: $maxRequestsPerHost" }
      synchronized(this) {
        field = maxRequestsPerHost
      }
      promoteAndExecute()
    }

maxRequests = 64 默认最大并发请求数量是64

maxRequestPerHost = 5 默认单个Host最大并发请求量是5

继续向下看,初始化了线程池,这个后面可以单独讲

@get:Synchronized
  @get:JvmName("executorService") val executorService: ExecutorService
    get() {
      if (executorServiceOrNull == null) {
        executorServiceOrNull = ThreadPoolExecutor(0, Int.MAX_VALUE, 60, TimeUnit.SECONDS,
            SynchronousQueue(), threadFactory("$okHttpName Dispatcher", false))
      }
      return executorServiceOrNull!!
    }

线程池初始化完成后,继续向下看

private val readyAsyncCalls = ArrayDeque()


private val runningAsyncCalls = ArrayDeque()


private val runningSyncCalls = ArrayDeque()

通过注释可以知道

readyAsyncCalls      等待的异步任务队列

runningAsyncCalls   在运行的异步任务队列

runningSyncalls       在运行的同步任务队列

这里的ArrayDeque是双端队列,什么是双端队列?

一般队列是FIFO,即first in first out,先进先出,栈是LIFO,last in fisrt out,后进先出,而双端队列结合两种数据结构的特性,可以做到两端进,可以两端弹出,也就是可以在队列的两端进行插入和删除操作。

那么第二个问题又来了,既然是双端队列,为什么选ArrayDeque?linkedList也是大小可变的双端队列,为什么没有选择它?

这个问题就涉及到链表和数组的区别了,各有优劣,说道这里可能已经知道接下来的分析,没错,ArrayDeque底层基于数组(循环数组)实现,linkedList底层是链表实现,数组的优势占用连续内存区域、随机读取效率高,但插入和删除效率低,而链表占用非连续内存,随机读取效率低,但插入和删除效率高,这样看来,貌似不分伯仲?

实际上,在OkHttp中,网络请求也是按照先来后到的执行,后来的需要在后面排队,每次执行过网络请求时,都要遍历readyAsyncCalls,把符合条件的网络请求加入runningAsyncCalls队列中,这时,数组中数据是连续存储在内存单元的,cpu寻找时数组就会更快,而且,连续的存储方式在垃圾回收GC时,效率会优于链表,所以,应该是综合性能,最终选择ArrayDeque

OKHttp涉及到的设计模式

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/630960.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号