• Connor学Android - OkHttp基本使用与源码解析


    在这里插入图片描述

    Learn && Live

    虚度年华浮萍于世,勤学善思至死不渝

    前言

    Hey,欢迎阅读Connor学Android系列,这个系列记录了我的Android原理知识学习、复盘过程,欢迎各位大佬阅读斧正!原创不易,转载请注明出处:http://t.csdn.cn/EVHjI,话不多说我们马上开始!

    1.使用

    异步GET请求

    (1)创建OkHttpClient对象

    (2)通过Builder模式创建Request对象,参数必须有个url参数,可以通过Request.Builder设置更多的参数比如:header、method等

    (3)通过request的对象去构造得到一个Call对象,Call对象有execute()和cancel()等方法。

    (4)以异步的方式去执行请求,调用的是call.enqueue,将call加入调度队列,任务执行完成会在Callback中得到结果。

    注意事项:

    • 异步调用的回调函数是在子线程,我们不能在子线程更新UI
    • onResponse回调有一个参数是response
      • 如果想获得返回的是字符串,可以通过response.body().string()
      • 如果获得返回的二进制字节数组,则调用response.body().bytes()
      • 如果想拿到返回的inputStream,则调response.body().byteStream(),之后通过IO的方式写文件

    异步POST请求

    与GET类似,只是需要传输实体

    通过FormBody,添加多个String键值对,最后为Request添加post方法并传入formBody

    2.基本对象介绍

    OkHttpClient

    创建 OkHttpClient 有两种方式:new 或 使用建造者模式为其设置一些参数,无论哪一种,实际上都是使用建造者模式完成的

    open class OkHttpClient internal constructor(builder: Builder) 
    	: Cloneable, Call.Factory, WebSocket.Factory {
    
      constructor() : this(Builder())
    
      class Builder constructor() {
      	//调度器
        internal var dispatcher: Dispatcher = Dispatcher()
        //连接池
        internal var connectionPool: ConnectionPool = ConnectionPool()
        //整体流程拦截器
        internal val interceptors: MutableList<Interceptor> = mutableListOf()
        //网络流程拦截器
        internal val networkInterceptors: MutableList<Interceptor> = mutableListOf()
        //流程监听器
        internal var eventListenerFactory: EventListener.Factory = EventListener.NONE.asFactory()
        //连接失败时是否重连
        internal var retryOnConnectionFailure = true
        //服务器认证设置
        internal var authenticator: Authenticator = Authenticator.NONE
        //是否重定向
        internal var followRedirects = true
        //是否从HTTP重定向到HTTPS
        internal var followSslRedirects = true
        //cookie设置
        internal var cookieJar: CookieJar = CookieJar.NO_COOKIES
        //缓存设置
        internal var cache: Cache? = null
        //DNS设置
        internal var dns: Dns = Dns.SYSTEM
        //代理设置
        internal var proxy: Proxy? = null
        //代理选择器设置
        internal var proxySelector: ProxySelector? = null
        //代理服务器认证设置
        internal var proxyAuthenticator: Authenticator = Authenticator.NONE
        //socket配置
        internal var socketFactory: SocketFactory = SocketFactory.getDefault()
        //https socket配置
        internal var sslSocketFactoryOrNull: SSLSocketFactory? = null
        internal var x509TrustManagerOrNull: X509TrustManager? = null
        internal var connectionSpecs: List<ConnectionSpec> = DEFAULT_CONNECTION_SPECS
        //协议
        internal var protocols: List<Protocol> = DEFAULT_PROTOCOLS
        //域名校验
        internal var hostnameVerifier: HostnameVerifier = OkHostnameVerifier
        internal var certificatePinner: CertificatePinner = CertificatePinner.DEFAULT
        internal var certificateChainCleaner: CertificateChainCleaner? = null
        //请求超时
        internal var callTimeout = 0
        //连接超时
        internal var connectTimeout = 10_000
        //读取超时
        internal var readTimeout = 10_000
        //写入超时
        internal var writeTimeout = 10_000
        internal var pingInterval = 0
        internal var minWebSocketMessageToCompress = RealWebSocket.DEFAULT_MINIMUM_DEFLATE_SIZE
        internal var routeDatabase: RouteDatabase? = null
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    Request

    同样是请求参数的配置类,也同样采用了建造者模式,但相比于OkHttpClientRequest就十分简单了,只有四个参数,分别是请求URL、请求方法、请求头、请求体

    class Request internal constructor(
      @get:JvmName("url") val url: HttpUrl,
      @get:JvmName("method") val method: String,
      @get:JvmName("headers") val headers: Headers,
      @get:JvmName("body") val body: RequestBody?,
      internal val tags: Map<Class<*>, Any>
    ) {
    
      open class Builder {
      	//请求的URL
        internal var url: HttpUrl? = null
        //请求方法,如:GET、POST..
        internal var method: String
        //请求头
        internal var headers: Headers.Builder
        //请求体
        internal var body: RequestBody? = null
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    Call

    请求调用接口,针对请求提供了一系列的操作方法,常见的有

    • request():返回 Request 对象
    • execute():同步请求,立即执行,请求失败抛出 IOException、执行过一次后再次执行抛出 IllegalStateException
    • enqueue():异步请求,将请求安排在将来某个时间点执行,若执行过一次后再次执行抛出 IllegalStateException
    • cancel():取消请求,注意已经完成的请求不能被取消
    • 利用工厂模式来让 OkHttpClient 、Request 对象来创建 newCall 对象
    interface Call : Cloneable {
      /** 返回发起此调用的原始请求 */
      fun request(): Request
    
      /**
       * 同步请求,立即执行。
       * 
       * 抛出两种异常:
       * 1. 请求失败抛出IOException;
       * 2. 如果在执行过一回的前提下再次执行抛出IllegalStateException;*/
      @Throws(IOException::class)
      fun execute(): Response
    
      /**
       * 异步请求,将请求安排在将来的某个时间点执行。
       * 如果在执行过一回的前提下再次执行抛出IllegalStateException */
      fun enqueue(responseCallback: Callback)
    
      /** 取消请求。已经完成的请求不能被取消 */
      fun cancel()
    
      /** 是否已被执行  */
      fun isExecuted(): Boolean
    
      /** 是否被取消   */
      fun isCanceled(): Boolean
    
      /** 一个完整Call请求流程的超时时间配置,默认选自[OkHttpClient.Builder.callTimeout] */
      fun timeout(): Timeout
    
      /** 克隆这个call,创建一个新的相同的Call */
      public override fun clone(): Call
    
      /** 利用工厂模式来让 OkHttpClient 来创建 Call对象 */
      fun interface Factory {
        fun newCall(request: Request): Call
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    RealCall

    (1)在使用时,通过 OkHttpClient 的 newCall() 方法创建一个 Call 对象,通过源码知道,newCall() 方法返回的是一个 RealCall 对象

    (2)RealCall 是 Call 接口的实现类,内部有三个参数:OkHttpClient、Request、是否使用 WebSocket(默认为 false)

    (3)RealCall 实现了上述 Call 接口的方法,因此可以调用 execute() 和 enqueue() 方法完成同步或异步请求方法

    override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)
    
    • 1

    AsyncCall

    (1)是 RealCall 的一个内部类,它继承自 NamedRunnable,NamedRunnable 实现了 Runnable 接口,它的作用有两个:

    • 采用模板方法的设计模式,让子类将具体的操作放在 execute() 方法中

    • 给线程指定一个名字,比如传入模块名称,方便监控线程的活动状态

    (2)AsyncCall 实现了 execute() 方法,用于异步请求下的调用,后续的流程分析会做详细介绍

    Dispatcher

    (1)调度器,用于调度 Call 对象

    (2)内部包含

    • 最大任务请求数限制,不可大于64

    • ThreadPoolExecutor 作为默认线程池,也可以通过构造方法执行其他线程池

    • ArrayDeque 类型的 readyAsyncCalls 已准备好的异步请求队列

    • ArrayDeque 类型的 runningAsyncCalls 正在运行的异步请求队列,包含取消但是还未 finish 的 AsyncCall

    • ArrayDeque 类型的 runningSyncCalls 正在运行的同步请求队列,包含取消但是还未 finish 的 RealCall

    class Dispatcher constructor() {
      @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!!
        }
    
      /** 已准备好的异步请求队列 */
      @get:Synchronized
      private val readyAsyncCalls = ArrayDeque<AsyncCall>()
    
      /** 正在运行的异步请求队列, 包含取消但是还未finish的AsyncCall */
      private val runningAsyncCalls = ArrayDeque<AsyncCall>()
    
      /** 正在运行的同步请求队列, 包含取消但是还未finish的RealCall */
      private val runningSyncCalls = ArrayDeque<RealCall>()
    
    ···省略代码···
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    总结

    对象作用
    Call请求调用接口,表示这个请求已经准备好可以执行,也可以被取消,只能执行一次
    RealCallCall 接口的具体实现类,是应用与网络层之间的连接桥,包含 OkHttpClient 和 Request 信息
    AsyncCall异步请求调用,其实就是个 Runnable,会被放到线程池中进行处理
    Dispatcher调度器,用来调度 Call 对象,同时包含线程池与异步请求队列,用来存放与执行 AsyncCall 对象
    Request请求类,包含 url、method、headers、body
    Response网络层返回的响应数据
    Callback响应回调函数接口,包含 onFailure 、onResponse 两个方法
    3.异步请求流程分析

    (1)异步请求首先会调用 RealCall 的 enqueue() 方法,这个方法内会做两件事

    • 检查是否重复调用了 enqueue() 方法
    • 调用 Dispatcher 的 enqueue() 方法,并将 AsyncCall 传进去

    (2)Dispatcher 的 enqueue() 方法内会将传入的 AsyncCall 加入 Dispatcher 的 ready 队列中,并调用 promoteAndExecutor() 方法

    (3)promoteAndExecutor() 方法内会遍历 ready 队列,取出每个 AsyncCall,并对当前的运行状态做双重约束

    • running 队列中的个数不可以超出最大请求数,若不满足则直接退出遍历循环,直至 running 队列有空位
    • AsyncCall 中的主机数不可以超出同一主机的最大任务数,若不满足则遍历下一个 AsyncCall

    若满足则将当前的 AsyncCall 添加到 running 队列中,并调用 AsyncCall 的 executeOn 方法

    (4)executeOn 方法中主要做三件事

    • 将当前遍历到的 AsyncCall 添加到线程池 executorService 中,调用 execute() 方法执行异步任务
    • 通过拦截器链来得到网络响应
    • 无论相应成功还是失败,都会调用 Dispatcher 中的 finished 方法

    (5)finished() 方法内主要做两件事

    • 将已执行完的 AsyncCall 退出 running 队列
    • 继续调用 promoteAndExecute() 方法遍历 ready 队列中的下一个 AsyncCall,形成完美闭环

    拦截器链

    (1)AsyncCall 的 executeOn() 方法中会通过拦截器链来得到网络相应,这里调用的是 getResponseWithInterceptorChain() 方法

    (2)这个方法主要做了两件事:

    • 创建拦截器集合,并将所有的拦截器
    • 创建职责链,并调用 proceed() 方法启动
      • 职责链采用的是职责链模式,使得每一个拦截器都有机会处理请求
        • 网络请求经过拦截器链的处理发送
        • 响应也会经过拦截器链的处理返回给应用层
      • proceed() 内会调用 RealInterceptorChain 的 copy() 方法,其内部会创建一个 RealInterceptorChain ,并循环处理每个拦截器
        • 首先获取当前要执行的拦截器
        • 运行当前拦截器,等当前拦截器处理完成后,会接着执行下一个拦截器的 proceed

    (3)各个拦截器的作用

    • interceptor:应用拦截器,通过 Client 设置
    • RetryAndFollowUpInterceptor:重试拦截器,负责网络请求中的重试和重定向。比如网络请求过程中出现异常时重试请求
    • BridgeInterceptor:桥接拦截器,用于桥接应用层和网络层的数据。请求时将应用层的数据类型转换为网络层的数据类型,响应时则相反
    • CacheInterceptor:缓存拦截器,负责读取和更新缓存。可以配置自定义的缓存拦截器
    • ConnectInterceptor:网络连接拦截器,其内部会获取一个连接
    • networkInterceptor:网络拦截器,通过 Client 设置
    • CallServerInterceptor:请求服务拦截器。拦截链中位于末尾的拦截器,用于向服务端发送数据并获取响应
  • 相关阅读:
    前端生成二维码
    Java 将Excel转为XML
    DolphinScheduler 1.x使用经验篇及bug解决篇
    测试5年,功能测试为何要转自动化测试,自动化测试又有哪些特点。
    [pytorch] 2D + 3D ResNet代码实现, 改写
    MIPI CSI接口调试方法: data rate计算
    nginx 配置git server http clone服务,并通过反向代理访问
    运维的利器–监控–zabbix–第二步:建设–部署zabbix agent--windows server系统
    JTabbedPane 右键标题关闭选项卡
    pcie 总结
  • 原文地址:https://blog.csdn.net/scottb/article/details/126758905