2024年最全【包学包会】从一次请求开始,深入探索OkHttp,2024年最新2024历年阿里Android面试真题

最后

Android学习是一条漫长的道路,我们要学习的东西不仅仅只有表面的 技术,还要深入底层,弄明白下面的 原理,只有这样,我们才能够提高自己的竞争力,在当今这个竞争激烈的世界里立足。

人生不可能一帆风顺,有高峰自然有低谷,要相信,那些打不倒我们的,终将使我们更强大,要做自己的摆渡人。

资源持续更新中,欢迎大家一起学习和探讨。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

fun intercept(chain: Chain): Response
}
复制代码

Interceptor只有一个方法,实现了intercept方法后需要调用传递进来的Chain,上面我们已经知道了这是下一个拦截器。调用了chain.proceed方法返回Response,将逻辑交由下一个拦截器处理。

Dispatcher

再回过头看异步请求,上面我们可以知道,一次异步请求最终是调用了dispatcher.enqueue的方法,那么Dispatcher负责了什么呢?

Dispatcher主要负责异步请求的执行逻辑。Dispatcher中可以定义maxRequests来管理最大并发请求数量,maxRequestsPerHost来确定单个host的最大并发请求数量。

internal fun enqueue(call: AsyncCall) {
synchronized(this) {
//加入队列
readyAsyncCalls.add(call)

// Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to
// the same host.
if (!call.call.forWebSocket) {
//找到此host存在的其他call
val existingCall = findExistingCallWithHost(call.host)
//如果找到了 复用其他call的计数器
if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)
}
}
//实际的去执行
promoteAndExecute()
}
复制代码

调用了enqueue方法后,会先上锁,然后在异步队列readyAsyncCalls中加入此请求,再检查当前请求的host有无其他call,找到了,则复用其他call的请求计数器。最后走到promoteAndExecute去执行。

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()

if (runningAsyncCalls.size >= this.maxRequests) break // 超过最大请求数量,跳出循环
if (asyncCall.callsPerHost.get() >= this.maxRequestsPerHost) continue //单个host请求上限,跳过此请求

i.remove()
asyncCall.callsPerHost.incrementAndGet()//cas 计数
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
}
复制代码

promoteAndExecute方法会遍历异步请求队列,如果当前并发请求数量上限了,则会跳出,不执行任何请求。如果一个host的并发请求数量达到了上限,会跳过此请求。最后,为可以执行的请求进行调用。如果用户没有自行设置线程池,则Dispatcher内部会创建一个的线程池用来执行异步网络请求。

fun executeOn(executorService: ExecutorService) {
client.dispatcher.assertThreadDoesntHoldLock()

var success = false
try {
//使用传入的线程池来执行
executorService.execute(this)
success = true
} catch (e: RejectedExecutionException) {
val ioException = InterruptedIOException(“executor rejected”)
ioException.initCause(e)
noMoreExchanges(ioException)
responseCallback.onFailure(this@RealCall, ioException)
} finally {
if (!success) {
//请求失败了也要通知dispatcher
client.dispatcher.finished(this) // This call is no longer running!
}
}
}
复制代码

上面我也说过了,AsyncCall本身实现了Runable接口,这里被执行之后,会调用run方法,执行内部逻辑,具体逻辑和同步请求的逻辑基本一致,这里就不再赘述。请求完成后,不管结果成功失败,都会调用Dispatcherfinished方法。

internal fun finished(call: AsyncCall) {
call.callsPerHost.decrementAndGet()//cas 计数
finished(runningAsyncCalls, call)
}

private fun finished(calls: Deque, call: T) {
val idleCallback: Runnable?
synchronized(this) {
//从队列中移除当前任务
if (!calls.remove(call)) throw AssertionError(“Call wasn’t in-flight!”)
idleCallback = this.idleCallback
}
//尝试执行其他任务
val isRunning = promoteAndExecute()

if (!isRunning && idleCallback != null) {
idleCallback.run()//如果当前闲置 进行通知
}
}
复制代码

finished方法被调用后会从请求队列中移除当前请求,再尝试执行剩余的请求。Dispatcher内部也维护了同步请求队列,当同步请求完成之后也会走类似的逻辑。

RetryAndFollowUpInterceptor

这个拦截器用来进行错误重试和重定向。拦截器内部是一个死循环。

try {
response = realChain.proceed(request)
newExchangeFinder = true
} catch (e: RouteException) {
// The attempt to connect via a route failed. The request will not have been sent.
if (!recover(e.lastConnectException, call, request, requestSendStarted = false)) {
throw e.firstConnectException.withSuppressed(recoveredFailures)
} else {
recoveredFailures += e.firstConnectException
}
newExchangeFinder = false
continue
} catch (e: IOException) {
// An attempt to communicate with a server failed. The request may have been sent.
if (!recover(e, call, request, requestSendStarted = e !is ConnectionShutdownException)) {
throw e.withSuppressed(recoveredFailures)
} else {
recoveredFailures += e
}
newExchangeFinder = false
continue
}
复制代码

网络请求的异常会被catch,然后会判断是否要重新进行请求。如果能正常走下去,则会对重定向相关进行判断,创建对应的请求。

ExchangeFinder

这个类在RetryAndFollowUpInterceptor中调用call.enterNetworkInterceptorExchange(request, newExchangeFinder)后被创建。这个类用来在RealConnectionPool连接池中找到一个当前请求可用的RealConnection,然后开启连接,进行接下来的IO操作。

ConnectInterceptor

这个拦截器会对指定的服务器打开连接,然后执行其他的拦截器

@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val realChain = chain as RealInterceptorChain
val exchange = realChain.call.initExchange(chain)//初始化Exchange
val connectedChain = realChain.copy(exchange = exchange)//为之后的责任链传入Exchange
return connectedChain.proceed(realChain.request)
}
复制代码

这个拦截器会调用RealCallinitExchange方法,并把当前的责任链传递过过去。

internal fun initExchange(chain: RealInterceptorChain): Exchange {
synchronized(this) {
check(expectMoreExchanges) { “released” }
check(!responseBodyOpen)
check(!requestBodyOpen)
}

val exchangeFinder = this.exchangeFinder!!
//用之前RetryAndFollowUpInterceptor传入的finder寻找编码器
val codec = exchangeFinder.find(client, chain)
//采用对应的编码器创建Exchange
val result = Exchange(this, eventListener, exchangeFinder, codec)
this.interceptorScopedExchange = result
this.exchange = result
synchronized(this) {
this.requestBodyOpen = true
this.responseBodyOpen = true
}

if (canceled) throw IOException(“Canceled”)
return result
}
复制代码

initExchange里会使用ExchangeFinder来寻找一个ExchangeCodec,这是一个网络请求的编码器,针对不同的协议会采用不同的方式来进行编码传输。

fun find(
client: OkHttpClient,
chain: RealInterceptorChain
): ExchangeCodec {
try {
//寻找一个健康的连接
val resultConnection = findHealthyConnection(
connectTimeout = chain.connectTimeoutMillis,
readTimeout = chain.readTimeoutMillis,
writeTimeout = chain.writeTimeoutMillis,
pingIntervalMillis = client.pingIntervalMillis,
connectionRetryEnabled = client.retryOnConnectionFailure,
doExtensiveHealthChecks = chain.request.method != “GET”
)
//创建对应的编码器
return resultConnection.newCodec(client, chain)
} catch (e: RouteException) {
trackFailure(e.lastConnectException)
throw e
} catch (e: IOException) {
trackFailure(e)
throw RouteException(e)
}
}
复制代码

ExchangeFinder的find方法会去尝试找到一个与服务器之间的连接。追踪findHealthyConnection代码我们会发现它内部是一个死循环,不断的调用findConnection方法去寻找一个可用的连接。findConnection的代码就比较长了,这里就不贴出来了。大概的逻辑就是优先从连接池中找连接,如果没有找到可用的连接,则会创建一个RealConnection对象,存入缓存池中。

RealConnection

RealConnection是OkHttp实际建立连接的地方。通过connect方法建立与服务器的链接。通过追踪源码我们会发现RealConnection底层还是通过Socket建立连接的。

@Throws(IOException::class)
private fun connectSocket(
connectTimeout: Int,
readTimeout: Int,
call: Call,
eventListener: EventListener
) {
val proxy = route.proxy
val address = route.address

val rawSocket = when (proxy.type()) {
Proxy.Type.DIRECT, Proxy.Type.HTTP -> address.socketFactory.createSocket()!!
else -> Socket(proxy)
}
this.rawSocket = rawSocket

eventListener.connectStart(call, route.socketAddress, proxy)
rawSocket.soTimeout = readTimeout
try {
//针对不同的平台进行适配
Platform.get().connectSocket(rawSocket, route.socketAddress, connectTimeout)
} catch (e: ConnectException) {
throw ConnectException(“Failed to connect to ${route.socketAddress}”).apply {
initCause(e)
}
}
try {
//使用OkIO开启io
source = rawSocket.source().buffer()
sink = rawSocket.sink().buffer()
} catch (npe: NullPointerException) {
if (npe.message == NPE_THROW_WITH_NULL) {
throw IOException(npe)
}
}
}
复制代码

CallServerInterceptor

这是所有拦截器中的最后一个拦截器。在这个拦截器里会进行IO操作与服务器交互。OkHttp底层使用了OkIO来进行IO操作。

@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val realChain = chain as RealInterceptorChain
val exchange = realChain.exchange!!//交换数据的桥梁,具体将具体交换逻辑分发到不同的实现
val request = realChain.request
val requestBody = request.body
val sentRequestMillis = System.currentTimeMillis()

var invokeStartEvent = true
var responseBuilder: Response.Builder? = null
var sendRequestException: IOException? = null
try {
//写请求头 最终会调用具体的ExchangeCodec去完成
exchange.writeRequestHeaders(request)

if (HttpMethod.permitsRequestBody(request.method) && requestBody != null) {
//遇到HTTP/1.1 中约定的Expect: 100-continue 会直接发起请求
if (“100-continue”.equals(request.header(“Expect”), ignoreCase = true)) {
exchange.flushRequest()//发送请求报文
responseBuilder = exchange.readResponseHeaders(expectContinue = true)//读取响应头
exchange.responseHeadersStart()//事件通知
invokeStartEvent = false
}
if (responseBuilder == null) {
if (requestBody.isDuplex()) {
//针对HTTP2协议的双工请求体 先发送请求头
exchange.flushRequest()
val bufferedRequestBody = exchange.createRequestBody(request, true).buffer()
requestBody.writeTo(bufferedRequestBody)//写入请求体
} else {
val bufferedRequestBody = exchange.createRequestBody(request, false).buffer()
requestBody.writeTo(bufferedRequestBody)//写入请求体
bufferedRequestBody.close()
}
} else {
//没有请求体
exchange.noRequestBody()
if (!exchange.connection.isMultiplexed) {
exchange.noNewExchangesOnConnection()
}
}
} else {
//没有请求体
exchange.noRequestBody()
}

if (requestBody == null || !requestBody.isDuplex()) {
exchange.finishRequest()//结束请求写入并发送
}
} catch (e: IOException) {
if (e is ConnectionShutdownException) {
throw e // No request was sent so there’s no response to read.
}
if (!exchange.hasFailure) {
throw e // Don’t attempt to read the response; we failed to send the request.
}
sendRequestException = e
}

try {
if (responseBuilder == null) {
//读取响应头
responseBuilder = exchange.readResponseHeaders(expectContinue = false)!!
if (invokeStartEvent) {
//事件通知
exchange.responseHeadersStart()
invokeStartEvent = false
}
}
//构建Response
var response = responseBuilder
.request(request)
.handshake(exchange.connection.handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build()
var code = response.code
if (code == 100) {
// Server sent a 100-continue even though we did not request one. Try again to read the
// actual response status.
responseBuilder = exchange.readResponseHeaders(expectContinue = false)!!
if (invokeStartEvent) {
exchange.responseHeadersStart()
}
//如果code 是100则重新读取
response = responseBuilder
.request(request)
.handshake(exchange.connection.handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build()
code = response.code
}
//事件通知
exchange.responseHeadersEnd(response)

response = if (forWebSocket && code == 101) {
// Connection is upgrading, but we need to ensure interceptors see a non-null response body.
response.newBuilder()
.body(EMPTY_RESPONSE)
.build()
} else {
//写入ResponseBody
response.newBuilder()
.body(exchange.openResponseBody(response))
.build()
}
//收到关闭连接的Header 关闭IO
if (“close”.equals(response.request.header(“Connection”), ignoreCase = true) ||
“close”.equals(response.header(“Connection”), ignoreCase = true)) {
exchange.noNewExchangesOnConnection()
}
if ((code == 204 || code == 205) && response.body?.contentLength() ?: -1L > 0L) {
throw ProtocolException(
“HTTP $code had non-zero Content-Length: ${response.body?.contentLength()}”)
}
//返回Response
return response
} catch (e: IOException) {
if (sendRequestException != null) {
sendRequestException.addSuppressed(e)
throw sendRequestException
}
throw e
}
}
复制代码

CallServerInterceptorintercept方法很长,不过逻辑并不复杂。大致流程如下:

  • 1、根据Request的配置写入请求行和请求头。
  • 2、根据Method判断是否支持请求体,如果支持则尝试写入请求体并发送请求报文,否则直接发送
  • 3、读取响应报文,构建Response
  • 4、读取响应体,为Response写入ResponseBody
  • 5、判断是否要关闭连接
  • 6、返回Response

因为CallServerInterceptor是最后一个Interceptor,所以返回的Response会一级一级的向上传递,最后用户就能拿到包装后的响应Response了。

BridgeInterceptor

这个拦截器是应用和网络交互的一个桥梁。首先他会获取Request里的信息,根据请求内容在Request中添加或者一些请求头,这些都是用户未感知到的。同时这个拦截器还会读取Cookie配置,如果有Cookie信息,也会通过请求头带到服务端。

Request信息完善后,会调用后续的责任链去处理完善的Request,并等待后续的返回。

val networkResponse = chain.proceed(requestBuilder.build())//等待响应

cookieJar.receiveHeaders(userRequest.url, networkResponse.headers)//处理Cookie

val responseBuilder = networkResponse.newBuilder()
.request(userRequest)//将原始请求信息放入Response

if (transparentGzip &&
“gzip”.equals(networkResponse.header(“Content-Encoding”), ignoreCase = true) &&
networkResponse.promisesBody()) {
val responseBody = networkResponse.body
if (responseBody != null) {
val gzipSource = GzipSource(responseBody.source())
val strippedHeaders = networkResponse.headers.newBuilder()
.removeAll(“Content-Encoding”)
.removeAll(“Content-Length”)
.build()//精简响应头
responseBuilder.headers(strippedHeaders)
val contentType = networkResponse.header(“Content-Type”)
//处理ResponseBody
responseBuilder.body(RealResponseBody(contentType, -1L, gzipSource.buffer()))
}
}
//返回完善的Response
return responseBuilder.build()
复制代码

收到后续的返回后,会对Response进行处理,并精简一些响应头。最终将完善的Response回传给用户。很多操作都是用户无感知的完成的,深藏功与名。

Response

这就没什么好说的了吧?在经过一些列链式调用和处理,最终用户可以拿到一个Response对象。在这里用户就可以拿到请求的各种信息以及相应,剩下的就交给用户自行处理了。在用户拿到Response后,一次网络请求也算完成啦!

总结

话不多说,先上图

总结:

各行各样都会淘汰一些能力差的,不仅仅是IT这个行业,所以,不要被程序猿是吃青春饭等等这类话题所吓倒,也不要觉得,找到一份工作,就享受安逸的生活,你在安逸的同时,别人正在奋力的向前跑,这样与别人的差距也就会越来越遥远,加油,希望,我们每一个人,成为更好的自己。

  • BAT大厂面试题、独家面试工具包,

  • 资料包括 数据结构、Kotlin、计算机网络、Framework源码、数据结构与算法、小程序、NDK、Flutter

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

都会淘汰一些能力差的,不仅仅是IT这个行业,所以,不要被程序猿是吃青春饭等等这类话题所吓倒,也不要觉得,找到一份工作,就享受安逸的生活,你在安逸的同时,别人正在奋力的向前跑,这样与别人的差距也就会越来越遥远,加油,希望,我们每一个人,成为更好的自己。

  • BAT大厂面试题、独家面试工具包,

  • 资料包括 数据结构、Kotlin、计算机网络、Framework源码、数据结构与算法、小程序、NDK、Flutter
    [外链图片转存中…(img-YYP3veO4-1715891389406)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 8
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值