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
Linux——Linux驱动之玩转SPI(上)Linux下SPI驱动框架简析及SPI设备驱动代码框架实现步骤
Linux——Linux驱动之玩转SPI(上)Linux下SPI驱动框架简析及SPI设备驱动代码框架实现步骤