腾讯面试官:OkHttp 用了那么久,你知道原理吗?
2021-05-29 08:02
标签:图片 ldl andriod 自己的 如何 row 函数 equals ida 很多朋友反映大厂的面试喜欢挖底层知识,像OkHttp这些都是必问的问题。这里就给大家分享一篇非常有帮助的技术文吧。 HTTP是现代应用程序网络的方式。这就是我们交换数据和媒体的方式。有效地执行HTTP可以使您的内容加载更快并节省带宽。 OkHttp是一个高效的HTTP库: 本文就以请求使用为入口,来深入学习下OkHttp。 Okhttp同步GET请求使用: 先来瞧瞧构建OkhttpClient的源码: OkhttpClient可以通过构造者配置参数来构建,也可以直接实例化,直接实例化其实也是内部调用构造者,只是传入的是默认builder。 发现返回的是RealCall,接下来去RealCall中看后续的execute执行方法 看起来比较精简,通过拦截器链获取网络响应,然后返回响应(拦截器链路在后续拦截器分析)。 Okhttp异步GET请求使用: 异步请求流程大致和同步请求相似,但是最后的执行方法是enqueue,并传入回调对象。 我们来看看源码: 内部调用了客户端的分发器的enqueue方法,并把AsyncCall(responseCallback)作为参数传入,AsyncCall是继承自Runnable,且是RealCall的内部类,我们先看Dispatcher.enqueue()方法 可以从上面代码看出,就是将符合条件的调用从readyAsyncCalls队列提升到runningAsyncCalls,并调用 AsyncCall的executeOn() 方法,把线程池传入。 使用线程池来执行自己,接下来就看run()方法,发现和同步请求一样,通过拦截器链获取网络响应,再调用回调对象的回调方法返回响应。 请求大致流程知道了,我们来看看重头戏,拦截器链里面做了什么操作。 我们在看下RealInterceptorChain的proceed方法: 接下来我们来具体看看各个拦截器的作用 RetryAndFollowUpInterceptor 处理错误恢复和重定向,它会判断错误是否满足条件进行重试,还有根据返回的响应判断是否需要重定向请求。 BridgeInterceptor 桥接应用层和网络层的代码,对用户的请求进行加工(如对请求头进行设置添加),也对网络响应做相应的处理(如解压服务端返回的 gzip 压缩数据)。 CacheInterceptor 承担着缓存的查找与保存的职责。根据策略判断是使用缓存还是走网络请求,对于返回的响应,满足条件则进行缓存。 ConnectInterceptor 主要是给网络请求提供一个连接,并交给下一个拦截器处理,这里还没有发送请求到服务器获取响应。在获取连接对象的时候,使用了连接池ConnectionPool来复用连接。 ConnectInterceptor 看似代码很少,其实代码都在深处,看下initExchange方法 ExchangeCodec持有连接,可通过其编码请求到服务端和获取服务端的响应并解码,我们依方法进入到最深处,看看是连接是如何获取的(代码已做简化处理)。 CallServerInterceptor 是真正向服务器发起请求并获取响应的,它是拦责任链的最后一个拦截器,拿到响应后返回给上一个拦截器。代码已做简化(省略了很多条件判断和处理)。 以上就是对OkHttp的源码解析,可以看出它是一个结构清晰的优质源码库,各个模块通过设计模式解耦。总结下流程:首先通过OkHttpClient对象调用newCall方法得到RealCall实例,再通过调用RealCall的execute方法或enqueue方法,这两个方法最终都会调用到getResponseWithInterceptorChain方法,运用责任链模式,开始一层层传入各个拦截器,每个拦截器都有着自己都职责,最终在CallServerInterceptor发出请求并获取响应,然后层层返回响应。 OkHttp源码 v4.9.1 腾讯面试官:OkHttp 用了那么久,你知道原理吗? 标签:图片 ldl andriod 自己的 如何 row 函数 equals ida 原文地址:https://www.cnblogs.com/chengsisi/p/14746420.html
原文地址:https://blog.csdn.net/qq_40861368/article/details/115832703
一、请求流程分析
1. 同步请求
// 新建一个Okhttp客户端(也可以通过OkHttpClient.Builder来构造)
OkHttpClient client = new OkHttpClient();
// 构造一个请求对象
Request request = new Request.Builder().url(url).build();
// 执行同步请求,返回响应
Response response = client.newCall(request).execute();
// 从响应体中获取数据
String str = response.body().string();
open class OkHttpClient internal constructor(
builder: Builder
) : Cloneable, Call.Factory, WebSocket.Factory {
//若直接实例化OkHttpClient,则调用主构造函数以默认Builder作为参数
constructor() : this(Builder())
// 通过builder中的值赋值
@get:JvmName("dispatcher") val dispatcher: Dispatcher = builder.dispatcher
@get:JvmName("connectionPool") val connectionPool: ConnectionPool = builder.connectionPool
...
class Builder constructor() {
//分发器
internal var dispatcher: Dispatcher = Dispatcher()
//连接池
internal var connectionPool: ConnectionPool = ConnectionPool()
//应用拦截器集合
internal val interceptors: MutableList
再来看看OkhttpClient的newCall方法
override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)
override fun execute(): Response {
//确认call没有执行过并置executed为true,否则抛出异常
check(executed.compareAndSet(false, true)) { "Already Executed" }
timeout.enter()
callStart()
try {
//标记执行中
client.dispatcher.executed(this)
//通过拦截器链获取网络响应
return getResponseWithInterceptorChain()
} finally {
//标记执行结束
client.dispatcher.finished(this)
}
}
2. 异步请求
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
// 执行异步请求,通过回调返回响应
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
// 从回调中通过响应体获取数据
String str = response.body().string();
}
});
override fun enqueue(responseCallback: Callback) {
//确认call没有执行过并置executed为true,否则抛出异常
check(executed.compareAndSet(false, true)) { "Already Executed" }
//监听回调
callStart()
//调用Dispatcher的enqueue方法,并传入一个AsyncCall对象
client.dispatcher.enqueue(AsyncCall(responseCallback))
}
class Dispatcher constructor() {
/** ‘异步准备执行‘队列 */
private val readyAsyncCalls = ArrayDeque()
/** ‘异步正在执行‘队列,包括取消但至今还没结束的 */
private val runningAsyncCalls = ArrayDeque()
/** ‘同步正在执行’队列*/
private val runningSyncCalls = ArrayDeque
我们来看看AsyncCall:
inner class AsyncCall(
private val responseCallback: Callback
) : Runnable {
...
fun executeOn(executorService: ExecutorService) {
client.dispatcher.assertThreadDoesntHoldLock()
var success = false
try {
//使用线程池执行自己的run()方法
executorService.execute(this)
success = true
} catch (e: RejectedExecutionException) {
...
//失败回调
responseCallback.onFailure(this@RealCall, ioException)
} 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
//回调成功
responseCallback.onResponse(this@RealCall, response)
} catch (e: IOException) {
if (signalledCallback) {
...
} else {
//回调失败
responseCallback.onFailure(this@RealCall, e)
}
} catch (t: Throwable) {
cancel()
if (!signalledCallback) {
...
//回调失败
responseCallback.onFailure(this@RealCall, canceledException)
}
throw t
} finally {
//标记结束
client.dispatcher.finished(this)
}
}
}
}
二、拦截器分析
@Throws(IOException::class)
internal fun getResponseWithInterceptorChain(): Response {
// 建立一个拦截器列表
val interceptors = mutableListOf
@Throws(IOException::class)
override fun proceed(request: Request): Response {
...
// 复制一个RealInterceptorChain,用于调用链中的下一个拦截器
val next = copy(index = index + 1, request = request)
val interceptor = interceptors[index]
@Suppress("USELESS_ELVIS")
// 调用下一个拦截器的intercept方法,获取response返回给上一个拦截器
val response = interceptor.intercept(next) ?: throw NullPointerException(
"interceptor $interceptor returned null")
...
return response
}
1. RetryAndFollowUpInterceptor
class RetryAndFollowUpInterceptor(private val client: OkHttpClient) : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val realChain = chain as RealInterceptorChain
var request = chain.request
val call = realChain.call
var followUpCount = 0
var priorResponse: Response? = null
var newExchangeFinder = true
var recoveredFailures = listOf
2. BridgeInterceptor
class BridgeInterceptor(private val cookieJar: CookieJar) : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
// 获取用户请求
val userRequest = chain.request()
// 真正发送的网络请求的构建者
val requestBuilder = userRequest.newBuilder()
// 用户请求的请求体
val body = userRequest.body
// 对请求头的设置
...
var transparentGzip = false
if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
transparentGzip = true
requestBuilder.header("Accept-Encoding", "gzip")
}
val cookies = cookieJar.loadForRequest(userRequest.url)
if (cookies.isNotEmpty()) {
requestBuilder.header("Cookie", cookieHeader(cookies))
}
if (userRequest.header("User-Agent") == null) {
requestBuilder.header("User-Agent", userAgent)
}
// 执行下一个拦截器,获取网络响应
val networkResponse = chain.proceed(requestBuilder.build())
cookieJar.receiveHeaders(userRequest.url, networkResponse.headers)
val responseBuilder = networkResponse.newBuilder()
.request(userRequest)
// 若因配置问题,服务端返回gzip压缩的数据,则做相应的解压缩
if (transparentGzip &&
"gzip".equals(networkResponse.header("Content-Encoding"), ignoreCase = true) &&
networkResponse.promisesBody()) {
val responseBody = networkResponse.body
if (responseBody != null) {
// GzipSource对象,用于解压
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")
responseBuilder.body(RealResponseBody(contentType, -1L, gzipSource.buffer()))
}
}
return responseBuilder.build()
}
}
3. CacheInterceptor
class CacheInterceptor(internal val cache: Cache?) : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val call = chain.call()
val cacheCandidate = cache?.get(chain.request())
val now = System.currentTimeMillis()
// 检查缓存策略
val strategy = CacheStrategy.Factory(now, chain.request(), cacheCandidate).compute()
// 若还需发送网络请求,则networkRequest不为空
val networkRequest = strategy.networkRequest
// 若存在可用缓存,则cacheResponse不为空
val cacheResponse = strategy.cacheResponse
...
// 如果我们被禁止使用网络,并且无可用缓存,则返回失败
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().also {
listener.satisfactionFailure(call, it)
}
}
// 如果不需要网络请求,缓存可用,则返回缓存
if (networkRequest == null) {
return cacheResponse!!.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build().also {
listener.cacheHit(call, it)
}
}
...
var networkResponse: Response? = null
try {
// 若无缓存可用,则执行下一个拦截器,获取响应
networkResponse = chain.proceed(networkRequest)
} finally {
if (networkResponse == null && cacheCandidate != null) {
cacheCandidate.body?.closeQuietly()
}
}
// 如果我们还有缓存响应,且网络响应code为304,则更新缓存响应,并返回
if (cacheResponse != null) {
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()
cache!!.trackConditionalCacheHit()
// 更新缓存
cache.update(cacheResponse, response)
return response.also {
listener.cacheHit(call, it)
}
} 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)) {
// 将网络响应写入缓存,并返回
val cacheRequest = cache.put(response)
return cacheWritingResponse(cacheRequest, response).also {
if (cacheResponse != null) {
listener.cacheMiss(call)
}
}
}
// 根据请求方法判断是否为无效请求,是则从缓存移除相对应响应
if (HttpMethod.invalidatesCache(networkRequest.method)) {
try {
cache.remove(networkRequest)
} catch (_: IOException) {
// cache无法被写
}
}
}
return response
}
}
4. ConnectInterceptor
object ConnectInterceptor : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val realChain = chain as RealInterceptorChain
// 查找新连接或池里的连接以承载即将到来的请求和响应
val exchange = realChain.call.initExchange(chain)
val connectedChain = realChain.copy(exchange = exchange)
return connectedChain.proceed(realChain.request)
}
}
internal fun initExchange(chain: RealInterceptorChain): Exchange {
...
// codec(ExchangeCodec) 是一个连接所用的编码解码器,用于编码HTTP请求和解码HTTP响应
val codec = exchangeFinder.find(client, chain)
// result(Exchange)是封装这个编码解码器的一个工具类,用于管理ExchangeCodec,处理实际的 I/O
val result = Exchange(this, eventListener, exchangeFinder, codec)
...
return result
}
private fun findConnection(): RealConnection {
// 1、复用当前连接
val callConnection = call.connection
if (callConnection != null) {
//检查这个连接是否可用和可复用
if (callConnection.noNewExchanges || !sameHostAndPort(callConnection.route().address.url)) {
toClose = call.releaseConnectionNoEvents()
}
return callConnection
}
//2、从连接池中获取可用连接
if (connectionPool.callAcquirePooledConnection(address, call, null, false)) {
val result = call.connection!!
eventListener.connectionAcquired(call, result)
return result
}
//3、从连接池中获取可用连接,通过一组路由routes(涉及知识点Http2多路复用)
if (connectionPool.callAcquirePooledConnection(address, call, routes, false)) {
val result = call.connection!!
return result
}
route = localRouteSelection.next()
// 4、创建新连接,进行tcp连接
val newConnection = RealConnection(connectionPool, route)
newConnection.connect
// 5、再获取一次连接,在新建连接过程中可能有其他竞争连接被创建了,如可用防止浪费
if (connectionPool.callAcquirePooledConnection(address, call, routes, true)) {
val result = call.connection!!
// 关闭刚刚创建的新连接
newConnection.socket().closeQuietly()
return result
}
//6、还是要使用创建的新连接,放入连接池,并返回
connectionPool.put(newConnection)
return newConnection
}
5. CallServerInterceptor
class CallServerInterceptor(private val forWebSocket: Boolean) : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val realChain = chain as RealInterceptorChain
// ConnectInterceptor获取到的,持有编码解码器
val exchange = realChain.exchange!!
val request = realChain.request
val requestBody = request.body
val sentRequestMillis = System.currentTimeMillis()
var responseBuilder: Response.Builder? = null
try {
// 写入请求头
exchange.writeRequestHeaders(request)
if (HttpMethod.permitsRequestBody(request.method) && requestBody != null) {
if (...) {
// 写入请求体
val bufferedRequestBody = exchange.createRequestBody(request, false).buffer()
requestBody.writeTo(bufferedRequestBody)
bufferedRequestBody.close()
} else {
...
}
} else {
// 无请求体
exchange.noRequestBody()
}
} catch (e: IOException) {...}
try {
if (responseBuilder == null) {
// 读取响应头
responseBuilder = exchange.readResponseHeaders(expectContinue = false)!!
if (invokeStartEvent) {
exchange.responseHeadersStart()
invokeStartEvent = false
}
}
// 构建响应
var response = responseBuilder
.request(request)
.handshake(exchange.connection.handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build()
var code = response.code
// 读取响应体
response = if (forWebSocket && code == 101) {
response.newBuilder()
.body(EMPTY_RESPONSE)
.build()
} else {
response.newBuilder()
.body(exchange.openResponseBody(response))
.build()
}
...
return response
} catch (e: IOException) {
...
}
}
}
总结
参考文献
《Android应用开发进阶》
Andriod 网络框架 OkHttp 源码解析
B站大厂面试真题解析视频合集
文章标题:腾讯面试官:OkHttp 用了那么久,你知道原理吗?
文章链接:http://soscw.com/index.php/essay/89026.html