OkHttp简析及实现

Posted xyTianZhao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OkHttp简析及实现相关的知识,希望对你有一定的参考价值。

OkHttp简析及实现

还记得 2015 年刚开始学习 android 那会,还在为 Eclipse 项目中集成 Afinal.jar 、 Volley.jar 爆红而发愁,一晃而过到现在的 2020 年,见证了 AndroidStudio 的兴起。就这短短的5年时间,很多工具和技术都进行了更新换代或者在升级

今天我们就看看经过官方承认并使用的网络请求框架 OkHttp

OkHttp简析

使用

接入 OkHttp

先来康康怎么接入,直接 gradle 中一行代码就搞定了。确实比之前还要下载相关 jar 包,然后在复制粘贴,然后在添加相关配置要简单的多了

implementation 'com.squareup.okhttp3:okhttp:4.8.1'

如果一行代码搞不定的话,那就再加几行 - -~,在项目的 build 文件中添加国内镜像

buildscript 
    repositories 
        maven  url 'http://maven.aliyun.com/nexus/content/repositories/jcenter' 
        maven  url "https://dl.bintray.com/thelasterstar/maven/" 
    

allprojects 
    repositories 
        maven  url 'http://maven.aliyun.com/nexus/content/repositories/jcenter' 
        maven  url "https://dl.bintray.com/thelasterstar/maven/" 
    

调用

调用就比较简单了,构建请求端,发送请求,回调接受请求结果。

//创建请求端
OkHttpClient()
        //创建请求体
        .newCall(Request.Builder().url("https://www.baidu.com").build())
        //创建请求完成后的回调接口
        .enqueue(object :Callback
            override fun onFailure(call: Call, e: IOException) 
                
            

            override fun onResponse(call: Call, response: Response) 
                
            
        )

使用方面无甚可说,主要就是在项目中应用的时候,尽量在 okhttp 之上在包装一层。这样如果后期需要更换底层 okhttp 的时候对于项目改动就比较小了。

下面就从源码简单分析一下 okhttp 的实现

源码简析

注意看题目,是源码简析,如果你想看完这篇文章就可以搞清楚 okhttp 的源码,那就要大失所望了。不是咱家不想写呀,主要是源码里面的细节处理和架构的设计,用文字表达就没有那个感觉了,还是得去撸源码,所以这里就只是把整个流程梳理一遍,具体的细枝末节都没有去说明。比如:建造者模式、责任链模式、线程池、线程安全等,在源码中都可以找到相应的影子。

闲话不多说,先来一张简易的调用流程图

从上面的调用流程图逐步分析源码,看看涉及到的类的相关调用

/**
 *  OkHttpClients Should Be Shared
 * 意思就是 OkHttpClient 这个玩意在应用中创建一次就可以进行全局共享了,没有比较多次创建。
 * 所以我们上面那个写法有点不规范哈,别纠结
 */
open class OkHttpClient internal constructor(
  builder: Builder
) : Cloneable, Call.Factory, WebSocket.Factory 
    //① 构造一个 OkHttpClient 请求端
    //这里的 builder 用到了建造者模式。里面主要是一些配置相关信息,感兴趣可以撸源码,这里不多说
    constructor() : this(Builder())

    /** Prepares the [request] to be executed at some point in the future. */
    //通过传进来的 request 请求体,构造一个真正的请求结构 RealCall ,将来便于添加到线程池中运行
    //② 调用 newCall 方法构造一个真正的请求体
    override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)


class RealCall(
  val client: OkHttpClient,
  /** The application's original request unadulterated by redirects or auth headers. */
  val originalRequest: Request,
  val forWebSocket: Boolean
) : Call 
  // ③ 调用该方法将异步请求 AsyncCall 加入线程池执行
  override fun enqueue(responseCallback: Callback) 
    check(executed.compareAndSet(false, true))  "Already Executed" 

    callStart()
    client.dispatcher.enqueue(AsyncCall(responseCallback))
  
  internal inner class AsyncCall(
    private val responseCallback: Callback
  ) : Runnable 
    
    override fun run() 
      threadName("OkHttp $redactedUrl()") 
        var signalledCallback = false
        timeout.enter()
        try 
          // ④ 线程执行 run 方法请求网络返回结果 response
          val response = getResponseWithInterceptorChain()
          signalledCallback = true
          // ⑤ 回调正确结果
          responseCallback.onResponse(this@RealCall, response)
         catch (e: IOException) 
          if (signalledCallback) 
            // Do not signal the callback twice!
            Platform.get().log("Callback failure for $toLoggableString()", Platform.INFO, e)
           else 
            // ⑤ 回调失败结果
            responseCallback.onFailure(this@RealCall, e)
          
         catch (t: Throwable) 
          cancel()
          if (!signalledCallback) 
            val canceledException = IOException("canceled due to $t")
            canceledException.addSuppressed(t)
            // ⑤ 回调取消结果
            responseCallback.onFailure(this@RealCall, canceledException)
          
          throw t
         finally 
          client.dispatcher.finished(this)
        
      
    
  

好了,okhttp 的调用流程这里就结束了,够简单吧,不过这是真的啊,就是这么个流程,剔除了一下繁杂的判断和细节处理。

当然,上面那个没有说到 okhttp 的精髓所在,就是步骤 ④ 中的 getResponseWithInterceptorChain 这个方法,下面我们进去看看。

class RealCall(val client: OkHttpClient, val originalRequest: Request, 
    val forWebSocket: Boolean) : Call 
  @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)
    // connect 请求拦截器
    interceptors += ConnectInterceptor
    if (!forWebSocket) 
      //网络拦截器 
      interceptors += client.networkInterceptors
    
    // 调用服务拦截器
    interceptors += CallServerInterceptor(forWebSocket)

    // 创建一个真正的拦截链条,并且将当前的拦截器集合传递进去
    val chain = RealInterceptorChain(
        call = this,interceptors = interceptors,index = 0,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)
      
    
  


/**
 * A concrete interceptor chain that carries the entire interceptor chain: all application
 * interceptors, the OkHttp core, all network interceptors, and finally the network caller.
 *
 * 一个具体的拦截器链,包含整个拦截器链:所有应用程序*拦截器,OkHttp核心,所有网络拦截器,最后是网络调用者。
 */
class RealInterceptorChain(...)
  @Throws(IOException::class)
  override fun proceed(request: Request): Response 
    ......

    calls++

    ......

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

    // 调用当前拦截器的 intercept 方法处理相关逻辑,并且将下一个拦截器传入,执行完成后返回 response
    @Suppress("USELESS_ELVIS")
    val response = interceptor.intercept(next) ?: throw NullPointerException(
        "interceptor $interceptor returned null")

    ......
    return response
  

上面就是 okhttp 的核心所在了,将一个网络请求的各个逻辑拆分成一个个拦截器去处理,比如重试机制、缓存机制、网络连接等。下面来一张图可能就比较直观了,依据整个责任链条,逐步处理自己相应的逻辑,并最终依据链条,将结果逐步返回。

简易版OkHttp

下面是根据上面的逻辑,实现了一个大致的流程,是相当的粗糙。我觉得通过 150 行代码能梳理清除相关逻辑就可以了,不喜请喷

package com.silence.okhttpdemo.ok

import java.lang.Exception
import java.util.concurrent.SynchronousQueue
import java.util.concurrent.ThreadFactory
import java.util.concurrent.ThreadPoolExecutor
import java.util.concurrent.TimeUnit


/**
 * Author silence.
 * Time:2020/8/19.
 * Desc:简易版 okHttp,只为说明其大致流程
 */
object OK 
    private val okHttp = OkHttp()

    fun newCall(request: Request) = RealCall(okHttp,request)

    fun addInterceptor(interceptor: Interceptor)
        okHttp.interceptors.add(interceptor)
    

class Request(val url:String,var header:String = "")

class Response
    var isCache = false
    var url=""
    var header = ""
    var body = ""
    var result = ""


interface Callback 
    fun onResponse(response: Response)


class Dispatcher
    private val executorService = ThreadPoolExecutor(0, Int.MAX_VALUE, 60, TimeUnit.SECONDS,
        SynchronousQueue(), ThreadFactory  runnable ->
            Thread(runnable, "customer Thread")
        
    )
    internal fun enqueue(call: RealCall.AsyncCall) 
        executorService.execute(call)
    


class OkHttp 
    val dispatcher = Dispatcher()
    val cacheMgr = CacheMgr()
    val interceptors = mutableListOf<Interceptor>()


class RealCall(private val okHttp: OkHttp,
               private val request: Request)

    fun enqueue(responseCallback: Callback)
        okHttp.dispatcher.enqueue(AsyncCall(responseCallback))
    

    fun enqueue() = getResponseWithInterceptorChain()

    internal fun getResponseWithInterceptorChain(): Response 
        val interceptors = mutableListOf<Interceptor>()
        interceptors += okHttp.interceptors
        interceptors += RetryAndFollowUpInterceptor()
        interceptors += CacheInterceptor(okHttp.cacheMgr)
        interceptors += CallServerInterceptor()
        val chain = RealInterceptorChain(interceptors,0,request)
        return chain.proceed(request)
    

    internal inner class AsyncCall(private val responseCallback: Callback) :Runnable
        override fun run() 
            responseCallback.onResponse(getResponseWithInterceptorChain())
        
    

interface Interceptor 
    fun intercept(chain: Chain): Response
    interface Chain 
        fun proceed(request: Request): Response
        fun request():Request
    


class RealInterceptorChain(
    private val interceptors: List<Interceptor>,
    private val index: Int,
    private val request: Request
) : Interceptor.Chain

    override fun proceed(request: Request): Response 
        val next = RealInterceptorChain(interceptors,index+1,request)
        val interceptor = interceptors[index]
        return interceptor.intercept(next)
    

    override fun request() = request


class RetryAndFollowUpInterceptor : Interceptor
    var retryCount = 0
    override fun intercept(chain: Interceptor.Chain): Response 
        val request = chain.request()
        while (true)
            try 
                return chain.proceed(request)
             catch (e:Exception)
                retryCount++
            
            if (retryCount > 3)
                val response = Response()
                response.result = "error"
                response.url = chain.request().url
                return response
            
        
    

class CacheMgr
    //这里肯定不能这么写昂,我这么写只是为了便于理解
    val cache = hashMapOf<String,Response>()

class CacheInterceptor(private val cacheMgr: CacheMgr) : Interceptor

    override fun intercept(chain: Interceptor.Chain): Response 
        var response = cacheMgr.cache[chain.request().url]
        if (response == null)
            response = chain.proceed(chain.request())
            cacheMgr.cache[chain.request().url] = response
         else 
            response.isCache = true
        
        return response
    

class CallServerInterceptor : Interceptor
    //注意这里,作为最后一个 Interceptor,这里没有在调用 chain.proceed 方法了
    // 所以这里就是最底部了,从这里开始逐级向上返回 response
    override fun intercept(chain: Interceptor.Chain): Response 
        val response = Response()
        response.body = "body"
        response.header = chain.request().header
        response.result = "ok"
        response.url = chain.request().url
        return response
    


在来康康怎么调用,基本上和 okHttp 调用方式差不多

class MainActivity : AppCompatActivity() 
    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        OK.addInterceptor(object :Interceptor
            override fun intercept(chain: Interceptor.Chain): Response 
                val request = chain.request()
                request.header = "customer header"
                return chain.proceed(request)
            
        )

       OkHttpClient()
               .newCall(
                       Request.Builder()
                               .url("https://www.baidu.com")
                               .build()
               ).enqueue(
                       object : Callback 
                           override fun onFailure(call: Call, e: IOException) 

                           
                           @Throws(IOException::class)
                           override fun onResponse(call: Call, response: Response) 
                           
                       
               )
    

    fun asnyc(view: View) 
        OK.newCall(Request("https://www.baidu.com"))
            .enqueue(object :Callback
                override fun onResponse(response: Response) 
                    Log.d("silence","url:$response.url , result:$response.result , header:$response.header , isCache:$response.isCache , body:$response.body")
                
            )
    
    fun Synchronize(view: View) 
        thread 
            val response = OK.newCall(Request("https://www.baidu.com")).enqueue()
            Log.d("silence","url:$response.url , result:$response.result , header:$response.header , isCache:$response.isCache , body:$response.body")
        
    

ok,到这基本上就大致理清整体思路了,上面没有说到的任何一个细枝末节都足以用一篇文章详细描述,太多了,还是自己去扒拉吧,read the fuck code,best wishes

以上是关于OkHttp简析及实现的主要内容,如果未能解决你的问题,请参考以下文章

超强解析环形队列,简析单项,双向队列及基础功能实现---风之java

简析iOS动画原理及实现——Core Animation

Linux——Linux驱动之玩转SPI(上)Linux下SPI驱动框架简析及SPI设备驱动代码框架实现步骤

Linux——Linux驱动之玩转SPI(上)Linux下SPI驱动框架简析及SPI设备驱动代码框架实现步骤

Linux——Linux驱动之玩转SPI(上)Linux下SPI驱动框架简析及SPI设备驱动代码框架实现步骤

优雅设计封装基于Okhttp3的网络框架:HttpHeader接口设计实现 及 ResponseRequest封装实现