欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

荐 框架和流程——OkHttp 源码详解(一)

程序员文章站 2022-07-10 16:31:18
前言OkHttp应该是目前Android平台上使用最为广泛的开源网络库了,Android 在6.0之后也将内部的HttpUrlConnection的默认实现替换成了OkHttp。网上很多分析OkHttp的,都是在总体流程上,没有那么的细致,甚至有的同学看完了文章,认为OkHttp没有DNS解析。我说有,他说没有,我说有,他说没有,我说有,他说没有,我就站起来了,很快啊,年轻人,一、鸟瞰OkHttp下面的代码是一个很简单的例子,一步一步分析,起内部的工作原理 OkHttpClient client...

前言

OkHttp应该是目前Android平台上使用最为广泛的开源网络库了,Android 在6.0之后也将内部的HttpUrlConnection的默认实现替换成了OkHttp。

网上很多分析OkHttp的,都是在总体流程上,没有那么的细致,甚至有的同学看完了文章,认为OkHttp没有DNS解析。所以本系列会深入源码,既掌握结构,也了解细节

一、鸟瞰OkHttp

下面的代码是一个很简单的例子,一步一步分析,起内部的工作原理

 OkHttpClient client = new OkHttpClient();
 // Create request for remote resource.
 Request request = new Request.Builder()
     .url(ENDPOINT)
     .build();
 // Execute the request and retrieve the response.
 Response response = client.newCall(request).execute()

OkHttp 的整体架构是很简单的,Request 作为请求, Response作为响应,在RealCall 中处理同步异步请求,处理过程就是一系列的拦截器。
荐
                                                        框架和流程——OkHttp 源码详解(一)
图片来自

open class OkHttpClient internal constructor(
  builder: Builder
) : Cloneable, Call.Factory, WebSocket.Factory {
  // 使用默认的Builder 来创建OkHttpClient
  constructor() : this(Builder())
      ...省略代码...
  init {
      ...省略代码...

	  //获取证书信任管理器	
      this.x509TrustManager = Platform.get().platformTrustManager()
      //用指定的证书信任管理器,来创建一个sslSocket工厂
      this.sslSocketFactoryOrNull =Platform.get().newSslSocketFactory(x509TrustManager!!)
      //省略与TLS握手无关的意外证书,并提取受信任的CA证书以使证书固定。
      this.certificateChainCleaner = CertificateChainCleaner.get(x509TrustManager!!)
      //用来限制哪些证书可以被信任
      this.certificatePinner = builder.certificatePinner
          .withCertificateChainCleaner(certificateChainCleaner!!)
    ...省略代码...

    verifyClientState()
  }

  /** Prepares the [request] to be executed at some point in the future. */
  override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)

      ...省略代码...
      
//Builder 模式,可以定制OkHttp
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()
    //事件监听,一些事件发生后,回调该接口,比如网络链接成功,tls握手成功,dns解析开始结束等,
    internal var eventListenerFactory: EventListener.Factory = EventListener.NONE.asFactory()
	//cookie
    internal var cookieJar: CookieJar = CookieJar.NO_COOKIES
    //缓存
    internal var cache: Cache? = null
    //解析dns的类
    internal var dns: Dns = Dns.SYSTEM
    //通过代理来访问网络,例如,socket代理,http代理等 ,通过http代理 访问https,需要建立通道 Tunnel
    internal var proxy: Proxy? = null
    //代理选择器
    internal var proxySelector: ProxySelector? = null
    internal var proxyAuthenticator: Authenticator = Authenticator.NONE
    //负责创建socket连接
    internal var socketFactory: SocketFactory = SocketFactory.getDefault()
    //负责创建SSLSocket连接 ,若无指定,在初始化OkHttp的时候 被赋值
    internal var sslSocketFactoryOrNull: SSLSocketFactory? = null
    //X509证书信任管理器,若无指定,在初始化OkHttp的时候 被赋值
    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
    //省略与TLS握手无关的意外证书,并提取受信任的CA证书以使证书固定。
    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
    //每一个Route 对应一个Url 链接
    internal var routeDatabase: RouteDatabase? = null

      ...省略代码...
  }


}

示例中,调用newCall后,创建了RealCall 对象,调用execute() 执行同步请求,调用enqueue 执行异步请求

同步操作


  //同步操作,
  override fun execute(): Response {
    check(executed.compareAndSet(false, true)) { "Already Executed" }

    timeout.enter()
    callStart()
    try {
      //这里的dispatcher 是在创建OkHttp 的时候创建的
      //因为这个是同步请求操作,所以executed 只是把RealCall对象放入到runningSyncCalls 堆中,表示正在进行
      client.dispatcher.executed(this)
      //所有的拦截器都在这里执行,OkHttp 的核心就是拦截器,所有的操作都是以拦截器的形式(责任链模式),例如Dns解析,socket连接,tls连接,缓存,网络请求,自定义拦截器等等
      return getResponseWithInterceptorChain()
    } finally {
      //把RealCall对象从runningSyncCalls 堆中删除,并调用promoteAndExecute() ,来执行已经准备好的异步操作
      client.dispatcher.finished(this)
    }
  }

异步操作


 // 异步操作,因为是异步的,所以需要设置回调接口
  override fun enqueue(responseCallback: Callback) {
    check(executed.compareAndSet(false, true)) { "Already Executed" }

    callStart()
    //用回调接口创建一个AsyncCall对象,下面介绍AsyncCall
    //enqueue 只是把该请求对象,放入readyAsyncCalls堆中,然后调用promoteAndExecute()
    client.dispatcher.enqueue(AsyncCall(responseCallback))
  }

下面到Dispatcher 中 去看看


  internal fun enqueue(call: AsyncCall) {
    synchronized(this) {
    // 把异步请求,加入到堆中
      readyAsyncCalls.add(call)
      ...省略代码...
    }
    //处理异步请求
    promoteAndExecute()
  }


  private fun promoteAndExecute(): Boolean {
    this.assertThreadDoesntHoldLock()

    val executableCalls = mutableListOf<AsyncCall>()
    val isRunning: Boolean
    synchronized(this) {
     //从堆中拿出异步请求对象
      val i = readyAsyncCalls.iterator()
      while (i.hasNext()) {
        val asyncCall = i.next()

        if (runningAsyncCalls.size >= this.maxRequests) break // Max capacity.
        if (asyncCall.callsPerHost.get() >= this.maxRequestsPerHost) continue // Host max capacity.

        i.remove()
        asyncCall.callsPerHost.incrementAndGet()
        //如果符合上面的两个条件,就把异步请求加入到executableCalls
        executableCalls.add(asyncCall)
        runningAsyncCalls.add(asyncCall)
      }
      isRunning = runningCallsCount() > 0
    }

    for (i in 0 until executableCalls.size) {
      val asyncCall = executableCalls[i]
      //在线程池中依次执行 asyncCall
      asyncCall.executeOn(executorService)
    }

    return isRunning
  }

异步请求对象AsyncCall


  internal inner class AsyncCall(
    private val responseCallback: Callback
  ) : Runnable {
      ...省略代码...

    /**
     * Attempt to enqueue this async call on [executorService]. This will attempt to clean up
     * if the executor has been shut down by reporting the call as failed.
     */
    fun executeOn(executorService: ExecutorService) {
      client.dispatcher.assertThreadDoesntHoldLock()

      var success = false
      try {
        // 通过线程池,来调用AsyncCall,它是继承自Runnable,真正的实现在run函数中
        executorService.execute(this)
        success = true
      } catch (e: RejectedExecutionException) {
        ...省略代码...
      } finally {
        if (!success) {
          client.dispatcher.finished(this) // This call is no longer running!
        }
      }
    }

    override fun run() {
      threadName("OkHttp ${redactedUrl()}") {
        var signalledCallback = false
        timeout.enter()
        try {
          //和同步请求一样的处理方式,返回处理结果
          val response = getResponseWithInterceptorChain()
          signalledCallback = true
          //通过回调接口来返回请求结果,这里实在线程中返回的,所以不能直接在回调接口中进行UI操作
          responseCallback.onResponse(this@RealCall, response)
        } catch (e: IOException) {
            ...省略代码...
        } finally {
          client.dispatcher.finished(this)
        }
      }
    }
  }

网络请求的大概流程,算是搞清楚了,接下来就来看看OkHttp核心——拦截器

二、OkHttp的核心:拦截器

这里主要分析,所有的拦截器是如何进行组织,链式调用的 ,具体每个拦截器的处理方式先不展开

一图胜千言,拦截器的调用,在拦截器A的任意位置,对用拦截器B,在B返回后,可继续执行A
荐
                                                        框架和流程——OkHttp 源码详解(一)
图片来源

从前面的同步请求和异步请求可以看出,最终都是要调用getResponseWithInterceptorChain()来处理,下面就来看看这个函数到底都做了些什么

  @Throws(IOException::class)
  internal fun getResponseWithInterceptorChain(): Response {
    // Build a full stack of interceptors.
    val interceptors = mutableListOf<Interceptor>()
    //自定义应用拦截器
    interceptors += client.interceptors
    //
    interceptors += RetryAndFollowUpInterceptor(client)
    //
    interceptors += BridgeInterceptor(client.cookieJar)
    //缓存拦截器
    interceptors += CacheInterceptor(client.cache)
    //链接拦截器,dns解析,socket链接,tls链接,代理处理等
    interceptors += ConnectInterceptor
    if (!forWebSocket) {
      //自定义网络拦截器
      interceptors += client.networkInterceptors
    }
    interceptors += CallServerInterceptor(forWebSocket)
	//创建一个RealInterceptorChain,把所有拦截器传入,算是拦截器处理的入口
    val chain = RealInterceptorChain(
        call = this, // 方便在拦截器中,获取当前的RealCall对象,因为有些操作是在RealCall对象里面的
        interceptors = interceptors, //传入拦截器集合
        index = 0, //默认从第一个拦截器开始,随着程序的运行,RealInterceptorChain中的这个变量会增加
        exchange = null,
        request = originalRequest,//网络请求,
        //以下是超时时间设置
        connectTimeoutMillis = client.connectTimeoutMillis,
        readTimeoutMillis = client.readTimeoutMillis,
        writeTimeoutMillis = client.writeTimeoutMillis
    )

    var calledNoMoreExchanges = false
    try {
      //开始执行 拦截器
      val response = chain.proceed(originalRequest)
      if (isCanceled()) {
        response.closeQuietly()
        throw IOException("Canceled")
      }
      return response
    } catch (e: IOException) {
      calledNoMoreExchanges = true
      throw noMoreExchanges(e) as Throwable
    } finally {
      if (!calledNoMoreExchanges) {
        noMoreExchanges(null)
      }
    }
  }

拦截器处理的核心,
1、这些拦截器需要按照顺序来依次执行,所以所有的拦截器必须有统一的接口,这样不管是什么拦截器,调用的方式都是一致的。(依赖倒置原则) 所有的拦截器都实现了Interceptor接口

2、 如何依次调用每个拦截器,其实 每个拦截器对应一个 RealInterceptorChain,拦截器在集合中的索引值 = RealInterceptorChain中的index,在调用拦截器 时,把RealInterceptorChain 对象next 传入,interceptor.intercept(next) 这样就可以在当前拦截器的任意位置,调用下一个拦截器了。

有同学可能有疑问,直接一个循环,遍历每个拦截器,调用每个Interceptor接口,这样不行吗?

这样没办法实现,在当前拦截器的任意位置调用下一个拦截器,每个拦截器的处理逻辑不一样,调用下一个拦截器的时机也不同

class RealInterceptorChain(
  internal val call: RealCall,
  private val interceptors: List<Interceptor>,
  private val index: Int,
  internal val exchange: Exchange?,
  internal val request: Request,
  internal val connectTimeoutMillis: Int,
  internal val readTimeoutMillis: Int,
  internal val writeTimeoutMillis: Int
) : Interceptor.Chain {

  private var calls: Int = 0

 // 创建一个RealInterceptorChain 对象,为了调用下一个拦截器 (集合中index对应的拦截器)
  internal fun copy(
    index: Int = this.index,
    exchange: Exchange? = this.exchange,
    request: Request = this.request,
    connectTimeoutMillis: Int = this.connectTimeoutMillis,
    readTimeoutMillis: Int = this.readTimeoutMillis,
    writeTimeoutMillis: Int = this.writeTimeoutMillis
  ) = RealInterceptorChain(call, interceptors, index, exchange, request, connectTimeoutMillis,
      readTimeoutMillis, writeTimeoutMillis)

   ...省略代码...
  override fun call(): Call = call

  override fun request(): Request = request

  // 上面调用了这个函数,从这里开始处理拦截器
  @Throws(IOException::class)
  override fun proceed(request: Request): Response {
    check(index < interceptors.size)

    calls++

    if (exchange != null) {
      check(exchange.finder.sameHostAndPort(request.url)) {
        "network interceptor ${interceptors[index - 1]} must retain the same host and port"
      }
      check(calls == 1) {
        "network interceptor ${interceptors[index - 1]} must call proceed() exactly once"
      }
    }

    // Call the next interceptor in the chain.
    //指定下一个拦截器的索引值,也就是当前拦截器索引值+1
    val next = copy(index = index + 1, request = request)
    //获取当前的拦截器
    val interceptor = interceptors[index]

    @Suppress("USELESS_ELVIS")
    //调用拦截器
    val response = interceptor.intercept(next) ?: throw NullPointerException(
        "interceptor $interceptor returned null")

    if (exchange != null) {
      check(index + 1 >= interceptors.size || next.calls == 1) {
        "network interceptor $interceptor must call proceed() exactly once"
      }
    }

    check(response.body != null) { "interceptor $interceptor returned a response with no body" }

    return response
  }
}

三、 关于自定义拦截器

在代码中,可以通过 addInterceptor() 和 addNetworkdInterceptor() 来添加自己的拦截器,分别叫做应用拦截器和网络拦截器,他们的调用时机如下图:

荐
                                                        框架和流程——OkHttp 源码详解(一)

getResponseWithInterceptorChain源码中可以看到,

  • 应用拦截器 interceptors 是最先被加入集合的,
  • 网络拦截器networkInterceptors 是在ConnectInterceptor 之后被添加的,

也就是说应用拦截器是最先被调用的,网络拦截器是在网络链接后才被调用,如果发生地址重定向,网络连接器会被多次调用

自定义拦截器的使用、区别、示例代码,可以查看这篇文章Interceptors拦截器——OkHttp3详细使用教程

结语

OkHttp的大概流程,就算是清楚了,接下来,就是针对每个拦截器的分析了。掌握了架构,再去补充细节,就容易掌握一些

本文地址:https://blog.csdn.net/xx326664162/article/details/107183019

相关标签: Android okhttp