一文摸懂OkHttp,android热修复视频

一文摸懂OkHttp,android热修复视频,第1张

一文摸懂OkHttp,android热修复视频

// 他会从正在执行运行的Call中先进行查找,再从准备执行的Call中查找

if (!call.call.forWebSocket) {

val existingCall = findExistingCallWithHost(call.host)

if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)

}

}

promoteAndExecute() // 1 ==>

}

// 1 ==>

private fun promoteAndExecute(): Boolean {

this.assertThreadDoesntHoldLock()

val executableCalls = mutableListOf()

val isRunning: Boolean

synchronized(this) {

val i = readyAsyncCalls.iterator()

// 将准备队列中的数据进行处理

while (i.hasNext()) {

val asyncCall = i.next()

// 正在运行的请求数量不能大于64个

if (runningAsyncCalls.size >= this.maxRequests) break // Max capacity.

// 可以存在的host数量为5个

if (asyncCall.callsPerHost.get() >= this.maxRequestsPerHost) continue // Host max capacity.

i.remove()

asyncCall.callsPerHost.incrementAndGet()

// 将要运行的放入运行队列中

executableCalls.add(asyncCall)

runningAsyncCalls.add(asyncCall)

}

// 用于判断当前队列中的是否还有请求正在进行中

isRunning = runningCallsCount() > 0

}

// 对每一个进入了运行队列中的请求进行正式运行

for (i in 0 until executableCalls.size) {

val asyncCall = executableCalls[i]

asyncCall.executeOn(executorService)

}

return isRunning

}

想来对整个的处理过程已经有一个比较详细的讲解了,但是我们还是没有看到数据的返回 *** 作,甚至说具体的运行,不过我们能够注意到一个中途意外冒出的变量executorService,这个变量是从哪里来的呢?

溯源我们能够发现,他在Dispatcher中就已经有过了初始化 *** 作。

@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!!

}

一看到要说ThreadPoolExecutor,哦哦哦哦!线程池,但是和什么线程池长得特别像呢?进入已经定义好的Executors类中查找,能够查找到如下的代码段:

public static ExecutorService newCachedThreadPool() {

return new ThreadPoolExecutor(0, Integer.MAX_VALUE,

60L, TimeUnit.SECONDS,

new SynchronousQueue());

}

是不是有点像呢?那好,我们就认定了它是我们的CachedThreadPool线程池。

ok!fine!用的线程池来进行异步 *** 作,那肯定就是说明里面有一个线程了,那这个线程是啥,我们是否心里有点数呢?如果没有,也没啥关系,下面我们将继续引出。

fun executeOn(executorService: ExecutorService) {

client.dispatcher.assertThreadDoesntHoldLock()

var success = false

try {

executorService.execute(this) // (1)

success = true

} catch (e: RejectedExecutionException) {

val ioException = InterruptedIOException(“executor rejected”)

ioException.initCause(e)

noMoreExchanges(ioException)

responseCallback.onFailure(this@RealCall, ioException) // (2)

} finally {

if (!success) {

client.dispatcher.finished(this) // (3)

}

}

}

那接下来就又不是什么大问题了,主要就看到我们的注释1、2、3。

    executorService.execute(this):对于线程池而言运行的显然是线程,而this几就是我们的AsyncCall,通过对AsyncCall的观察我们也是能够得知它是继承了Runnable的,所以异步进行的 *** 作来源我们也已经清楚了。

    responseCallback.onFailure(),也就是通过我们传入的Callback接收数据的错误反馈。

    client.dispatcher.finished(this):为什么需要这个呢?其实他原本有这样的一段英文注释,This call is no longer running!,也就是说明这个函数是为了通知Dispatcher我们的AsyncCall已经完成了运行。

又开始有问题了吧,看着就着急。咋就没看到responseCallback()的onResponse方法的使用呢???

那我们做一个猜测吧,其实我看了一下基本也是正解了。我们的不是Runnable嘛,而数据是放在线程池中run()来运行的,那么onResponse()方法的出现应该是在run()的这个函数中了。接下来我们继续收看代码

override fun run() {

threadName(“OkHttp ${redactedUrl()}”) {

var signalledCallback = false

timeout.enter()

try {

val response = getResponseWithInterceptorChain() // (1)

signalledCallback = true

responseCallback.onResponse(this@RealCall, response) //(2)

} catch (e: IOException) {

// 。。。。。

responseCallback.onFailure(this@RealCall, e)

} catch (t: Throwable) {

// 。。。。。

responseCallback.onFailure(this@RealCall, e)

} finally {

client.dispatcher.finished(this)

}

}

}

在这里的注释(2)中,我们很幸运的看到了onResponse()的方法调用了。好那接下来就是下一个问题了,Response是从哪里来的????

Response的诞生


上面不是写着嘛??getResponseWithInterceptorChain()这个函数里来的呗。哇哦!!没错了,那它是怎么来的? 樂樂樂

又要看代码了,好烦好烦。。。

internal fun getResponseWithInterceptorChain(): Response {

// Build a full stack of interceptors.

val interceptors = mutableListOf()

// 对应着我们刚开始自定义的拦截器

interceptors += client.interceptors

interceptors += RetryAndFollowUpInterceptor(client)

interceptors += BridgeInterceptor(client.cookieJar)

interceptors += CacheInterceptor(client.cache)

interceptors += ConnectInterceptor

// 我们之前上面也出现过forWebSocket这个flag

// 其实它是okhttp为了长连接而准备的

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

)

val response = chain.proceed(originalRequest)

return response

}

为了尽量让代码简洁明了,我截取了一些关键代码,以供参考。

其实他就是通过一堆的拦截器来获取数据的,但是显然这里不是终点站,因为我们看到的return中就还是一个函数,说明答案还在这个函数中。通过观察我们很容易得知,这个的 *** 作的具体类是一个叫做RealInterceptorChain的类。

override fun proceed(request: Request): Response {

// 不断调用下一个拦截器对相应的数据进行返回

val next = copy(index = index + 1, request = request)

val interceptor = interceptors[index]

val response = interceptor.intercept(next)

return response

}

如图所示,哪个拦截器能拦截成功,就会返回我们需要的数据Response,当然这个数据你需要注意,并不一定是成功的数据,一般来说数据成功的获取都需要走到我们的响应拦截器之后才能真正的成功。

CacheInterceptor缓存拦截器的源码解读


这里我们需要重点讲解一下CacheInterceptor这个类,我们截取他的intercept()方法,因为里面涉及了我们面试时可能会频繁使用的响应码

override fun intercept(chain: Interceptor.Chain): Response {

// 依据我们传入的request得到cache中缓存的response

val cacheCandidate = cache?.get(chain.request())

val now = System.currentTimeMillis()

// 获取当前的这个请求是网络请求、数据缓存的状况

val strategy = CacheStrategy.Factory(now, chain.request(), cacheCandidate).compute()

val networkRequest = strategy.networkRequest

val cacheResponse = strategy.cacheResponse

cache?.trackResponse(strategy)

if (cacheCandidate != null && cacheResponse == null) {

// The cache candidate wasn’t applicable. Close it.

cacheCandidate.body?.closeQuietly()

}

// 本地查询到的网络请求和缓存数据皆为空的情况下

// 爆HTTP_GATEWAY_TIMEOUT,网关超时的错误

if (networkRequest == null && cacheResponse == null) {

return Response.Builder()

.request(chain.request())

.protocol(Protocol.HTTP_1_1)

.code(HTTP_GATEWAY_TIMEOUT)

.message(“Unsatisfiable Request (only-if-cached)”)

.body(EMPTY_RESPONSE)

.sentRequestAtMillis(-1L)

.receivedResponseAtMillis(System.currentTimeMillis())

.build()

}

// 没有网络情况下,直接使用我们本地的数据缓存

if (networkRequest == null) {

return cacheResponse!!.newBuilder()

.cacheResponse(stripBody(cacheResponse))

.build()

}

// 调动责任链中下一轮的拦截器,来获取数据

var networkResponse: Response? = null

try {

networkResponse = chain.proceed(networkRequest)

} finally {

// If we’re crashing on I/O or otherwise, don’t leak the cache body.

if (networkResponse == null && cacheCandidate != null) {

cacheCandidate.body?.closeQuietly()

}

}

// 观察我们本地是否存在数据缓存

if (cacheResponse != null) {

// HTTP_NOT_MODIFIED:304,说明我们本地的缓存是最新的

// 没有必要将数据从服务器拉取进行更新了

if (networkResponse?.code == HTTP_NOT_MODIFIED) {

val response = cacheResponse.newBuilder()

.headers(combine(cacheResponse.headers, networkResponse.headers))

.sentRequestAtMillis(networkResponse.sentRequestAtMillis)

.receivedResponseAtMillis(networkResponse.receivedResponseAtMillis)

.cacheResponse(stripBody(cacheResponse))

.networkResponse(stripBody(networkResponse))

.build()

networkResponse.body!!.close()

// Update the cache after combining headers but before stripping the

// Content-Encoding header (as performed by initContentStream()).

cache!!.trackConditionalCacheHit()

cache.update(cacheResponse, response)

return response

} else {

cacheResponse.body?.closeQuietly()

}

}

val response = networkResponse!!.newBuilder()

.cacheResponse(stripBody(cacheResponse))

.networkResponse(stripBody(networkResponse))

.build()

// 更新我们本地的缓存数据

if (cache != null) {

if (response.promisesBody() && CacheStrategy.isCacheable(response, networkRequest)) {

// Offer this request to the cache.

val cacheRequest = cache.put(response)

return cacheWritingResponse(cacheRequest, response)

写在最后

最后我想说:对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们!

这里附上上述的技术体系图相关的几十套腾讯、头条、阿里、美团等公司2021年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。

相信它会给大家带来很多收获:

当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。

// Offer this request to the cache.

val cacheRequest = cache.put(response)

return cacheWritingResponse(cacheRequest, response)

写在最后

最后我想说:对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们!

这里附上上述的技术体系图相关的几十套腾讯、头条、阿里、美团等公司2021年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。

相信它会给大家带来很多收获:

[外链图片转存中…(img-0D6DDneG-1643538945344)]

[外链图片转存中…(img-mxaGQfja-1643538945345)]

当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5716490.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-18
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存