快捷搜索:  汽车  科技

okhttp使用哪些设计模式(字节跳动一面之Okhttp的五大拦截器面试总结)

okhttp使用哪些设计模式(字节跳动一面之Okhttp的五大拦截器面试总结)Okhttp支持五大拦截器使用,通过拦截器构建一条接收者对象链,其调用顺序通过责任链模式进行链式调用,每个接收者对象中都包含有另一个接收者的引用。如果当前接收者不处理网络请求,就会调用下一个接收者处理。2 .责任链模式3 .发起网络请求1 .建造者模式request在创建网络请求体时,使用建造者设计模式封装多种属性,在不同的使用场景下通过Build一步一步构建出所需要的请求对象。

本文通过在字节面试遇到的问题总结而出,如有不对地方,请及时批评指正。篇幅较长,请耐心阅读。

简介

Okhttp是目前一个比较流行的高效网络请求框架,支持多种网络请求方式,功能强大。如下图所示:

okhttp使用哪些设计模式(字节跳动一面之Okhttp的五大拦截器面试总结)(1)

使用步骤

1 .创建okhttpClient实例对象

okhttp使用哪些设计模式(字节跳动一面之Okhttp的五大拦截器面试总结)(2)

2 .创建一个网络请求

okhttp使用哪些设计模式(字节跳动一面之Okhttp的五大拦截器面试总结)(3)

3 .发起网络请求

okhttp使用哪些设计模式(字节跳动一面之Okhttp的五大拦截器面试总结)(4)

源码分析设计模式

1 .建造者模式

request在创建网络请求体时,使用建造者设计模式封装多种属性,在不同的使用场景下通过Build一步一步构建出所需要的请求对象。

okhttp使用哪些设计模式(字节跳动一面之Okhttp的五大拦截器面试总结)(5)

2 .责任链模式

Okhttp支持五大拦截器使用,通过拦截器构建一条接收者对象链,其调用顺序通过责任链模式进行链式调用,每个接收者对象中都包含有另一个接收者的引用。如果当前接收者不处理网络请求,就会调用下一个接收者处理。

构建拦截链

okhttp使用哪些设计模式(字节跳动一面之Okhttp的五大拦截器面试总结)(6)

3 .策略模式

在拦截器CacheInterceptor中,响应数据的选择中使用了策略模式,选择缓存数据还是选择网络访问。CacheInterceptor根据一个缓存策略,来决定选择缓存数据,还是网络请求数据。

okhttp使用哪些设计模式(字节跳动一面之Okhttp的五大拦截器面试总结)(7)

同步请求

okhttp使用哪些设计模式(字节跳动一面之Okhttp的五大拦截器面试总结)(8)

1.发起同步网络请求。

okHttpClient.newCall(request).execute()

2.将网络请求体封装成RealCall,真正发起网络请求的是RealCall。

override fun newCall(request: Request): Call = RealCall(this request forWebSocket = false)

3.调用RealCallexecute进行同步请求。

override fun execute(): Response { //超时取消 timeout.enter() //日志监听 callStart() try { //调用Dispatcher分发器进行同步请求 client.dispatcher.executed(this) //执行拦截器返回请求结果 return getResponseWithInterceptorChain() } finally { //完成请求将任务从队列中移除 client.dispatcher.finished(this) } }

4.将请求任务放进同步请求队列runningSyncCalls。

@synchronized internal fun executed(call: RealCall) { runningSyncCalls.add(call) }

5.执行RealInterceptorChain拦截器链进行网络请求。

internal fun getResponseWithInterceptorChain(): Response { // 创建拦截器集合 val interceptors = mutableListOf<Interceptor>() //添加用户自定义拦截器 interceptors = client.interceptors //添加重试重定向拦截器 interceptors = RetryAndFollowUpInterceptor(client) //添加桥接拦截器 interceptors = BridgeInterceptor(client.cookieJar) //添加缓存拦截器 interceptors = CacheInterceptor(client.cache) //添加连接拦截器 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 ) try { //开始网络请求 val response = chain.proceed(originalRequest) //返回请求结果 return response } }异步请求

okhttp使用哪些设计模式(字节跳动一面之Okhttp的五大拦截器面试总结)(9)

1.发起异步请求。

okHttpClient.newCall(request).enqueue( object :Callback{ override fun onFailure(call: Call e: IOException) { } override fun onResponse(call: Call response: Response) { } })

2.调用分发器dispatcher.enqueue执行网络请求。

override fun enqueue(responseCallback: Callback) { check(executed.compareAndSet(false true)) { "Already Executed" } callStart() //真正执行网络请求 client.dispatcher.enqueue(AsyncCall(responseCallback)) }

3.将异步请求放入异步等待队列readyAsyncCalls

internal fun enqueue(call: AsyncCall) { synchronized(this) { //将任务添加异步等待队列 readyAsyncCalls.add(call) if (!call.call.forWebSocket) { //判断当前主机的请求是否存在 val existingCall = findExistingCallWithHost(call.host) if (existingCall != null) call.reuseCallsPerHostFrom(existingCall) } } //执行请求 promoteAndExecute() }

4.从readyAsyncCalls异步等待队列中取出请求任务,放进线程池executorService执行。

private fun promoteAndExecute(): Boolean { ... //定义可执行任务集合 val executableCalls = mutableListOf<AsyncCall>() 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 }

5.线程池的执行体调用getResponseWithInterceptorChain返回数据。

override fun run() { ........... try { //开始网络请求 val response = getResponseWithInterceptorChain() //返回请求结果数据 responseCallback.onResponse(this@RealCall response) } catch (e: IOException) { } catch (t: Throwable) { //抛出异常立即取消操作 cancel() ........... } finally { //不管请求是否成功,都要进行finish操作 client.dispatcher.finished(this) } } } }

6.调用拦截器链RealInterceptorChain开始请求网络。

internal fun getResponseWithInterceptorChain(): Response { // 创建拦截器集合 val interceptors = mutableListOf<Interceptor>() //添加用户自定义拦截器 interceptors = client.interceptors //添加重试重定向拦截器 interceptors = RetryAndFollowUpInterceptor(client) //添加桥接拦截器 interceptors = BridgeInterceptor(client.cookieJar) //添加缓存拦截器 interceptors = CacheInterceptor(client.Cache) //添加连接拦截器 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 ) try { //开始网络请求 val response = chain.proceed(originalRequest) //返回请求结果 return response } }拦截器详解

OKhttp不管是同步请求还是异步请求最终都是调用拦截器的getResponseWithInterceptorChain()完成真正的网络请求操作。

1.getResponseWithInterceptorChain()

internal fun getResponseWithinterceptorChain(): Response { // 创建拦截器集合 val interceptors = mutableListOf<Interceptor>() //添加用户自定义拦截器 interceptors = client.interceptors //添加重试重定向拦截器 interceptors = RetryAndFollowUpInterceptor(client) //添加桥接拦截器 interceptors = BridgeInterceptor(client.cookieJar) //添加缓存拦截器 interceptors = CacheInterceptor(client.cache) //添加连接拦截器 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 ) try { //开始网络请求 val response = chain.proceed(originalRequest) //返回请求结果 return response } }

可以看出这个方法的核心就是拦截器集合interceptors,首先将client.interceptors全部加入其中接着还创建并添加了BridgeInterceptorCacheInterceptorConnectInterceptorCallServerInterceptor ,最后通过RealInterceptorChaindproceed(originalRequest) 来执行整个interceptorchain ,那么整个网络请求的重点就是必须清楚拦截器是怎么完成网络请求的?我们接着往下看。

2.RealInterceptorChaind.proceed

verride fun proceed(request: Request): Response { //检查当前执行的下标是否小于拦截器集合长度 check(index < interceptors.size) //计数 calls ............. // 获取下一个拦截器, val next = copy(index = index 1 request = request) //取出当前拦截器 val interceptor = interceptors[index] //返回下一个拦截器的执行结果 @Suppress("USELESS_ELVIS") val response = interceptor.intercept(next) ?: throw NullPointerException( "interceptor $interceptor returned null") if (exchange != null) { //检查拦截器是否已经执行完了 check(index 1 >= interceptors.size || next.calls == 1) { "network interceptor $interceptor must call proceed() exactly once" } } //检查请求是否成功返回数据 check(response.body != null) { "interceptor $interceptor returned a response with no body" } return response }

从这段代码实现可以看出,按照拦截器添加到 interceptors 集合的顺序,逐个往下调用拦截器的intercept()方法,所以在前面的拦截器会先被调用。

RetryAndFollowUpInterceptor失败重定向拦截器

override fun intercept(chain: Interceptor.Chain): Response { val realChain = chain as RealInterceptorChain var request = chain.request val call = realChain.call var followUpCount = 0 //统计重定向次数,不能大于20 var priorResponse: Response? = null var newExchangeFinder = true //定义一个失败恢复集合 var recoveredFailures = listOf<IOException>() //进入死循环 while (true) { .................... try { //取消网络请求 if (call.isCanceled()) { throw IOException("Canceled") } try { //调用下一个interceptor的来获得响应内容 response = realChain.proceed(request) } catch (e: RouteException) { ...... //抛出异常继续重试 continue } catch (e: IOException) { ...... //抛出异常继续重试 continue } //如果请求任务存在,则创建response if (priorResponse != null) { response = response.newBuilder() .priorResponse(priorResponse.newBuilder() .body(null) .build()) .build() } //重新发起请求 val followUp = followUpRequest(response exchange) ...................... //如果重试次数大于20次 则退出循环 if ( followUpCount > MAX_FOLLOW_UPS) { throw ProtocolException("Too many follow-up requests: $followUpCount") } request = followUp priorResponse = response } finally { call.exitNetworkInterceptorExchange(closeActiveExchange) } } }

RetryAndFollowUpInterceptor这个拦截器 主要负责错误处理和重定向等问题,比如链接错误、IO异常。

等。接下来就执行到BridgeInterceptor。

BridgeInterceptor桥接拦截器

override fun intercept(chain: Interceptor.Chain): Response { val userRequest = chain.request() //用户请求 val requestBuilder = userRequest.newBuilder() //构建请求 //请求体 val body = userRequest.body if (body != null) { val contentType = body.contentType() if (contentType != null) { requestBuilder.header("Content-Type" contentType.toString()) } //添加必要的请求头 val contentLength = body.contentLength() if (contentLength != -1L) { requestBuilder.header("Content-Length" contentLength.toString()) requestBuilder.removeHeader("Transfer-Encoding") } else { requestBuilder.header("Transfer-Encoding" "chunked") requestBuilder.removeHeader("Content-Length") } } ................................ //设置压缩编码类型 var transparentGzip = false if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) { transparentGzip = true requestBuilder.header("Accept-Encoding" "gzip") } //设置cookie 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()) .............. return responseBuilder.build() }

在这个BridgeInterceptor桥接拦截器中,okhttp为我们添加了必要请求头信息Content-Type、Content-Length、cookie、Connection、Host、Accept-Encoding,gzip处理等。负责把用户构造的请求转换为发送给服务器的请求,把服务器返回的响应结果进行解压处理。

CacheInterceptor缓存拦截器

override fun intercept(chain: Interceptor.Chain): Response { val call = chain.call() //获取缓存 val cacheCandidate = cache?.get(chain.request()) ............. val networkRequest = strategy.networkRequest 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) } } //缓存不为空,拿缓存 if (cacheResponse != null) { listener.cacheConditionalHit(call cacheResponse) } else if (cache != null) { listener.cacheMiss(call) } var networkResponse: Response? = null //调用下一个拦截器执行 networkResponse = chain.proceed(networkRequest) //有网络缓存直接拿网络缓存数据. 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() ............. } else { cacheResponse.body?.closeQuietly() } } ................... return response }

CacheInterceptor拦截器主要工作是读取缓存和更新缓存,如果有缓存并且缓存可用,那就使用缓存,否则进行调用下一个拦截器ConnectionInterceptor 进行网络请求,并将响应内容缓存。

ConnectionInterceptor 连接拦截器

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

ConnectionInterceptor 拦截器主要是打开一个到目标服务器的 connection并调用下一个拦截器 CallServerInterceptor ,并向服务器发起真正的网络请求。

CallServerInterceptor

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 responseBuilder: Response.Builder? = null //判断请求方法get还是post,且请求体不能为空 if (HttpMethod.permitsRequestBody(request.method) && requestBody != null) { //校验工作 ...................... //构建真正的网络请求 var response = responseBuilder .request(request) .handshake(exchange.connection.handshake()) .sentRequestAtMillis(sentRequestMillis) .receivedResponseAtMillis(System.currentTimeMillis()) .build() var code = response.code //相应码处理 if (code == 100) { responseBuilder = exchange.readResponseHeaders(expectContinue = false)!! if (invokeStartEvent) { exchange.responseHeadersStart() } //读取相应 response = responseBuilder .request(request) .handshake(exchange.connection.handshake()) .sentRequestAtMillis(sentRequestMillis) .receivedResponseAtMillis(System.currentTimeMillis()) .build() code = response.code } ............ //请求头校验 if ("close".equals(response.request.header("Connection") ignoreCase = true) || "close".equals(response.header("Connection") ignoreCase = true)) { exchange.noNewExchangesOnConnection() } ........... return response }

CallServerInterceptor 服务器请求拦截器才是真正的进行网络数据请求。整个调用流程最终都是将请求任务交给CallServerInterceptor 处理,并返回服务器返回结果。

从整个请求流程来看,Okhttp的拦截器才是整个网络框架的核心。整个Okhttp请求流程如下图所示:

okhttp使用哪些设计模式(字节跳动一面之Okhttp的五大拦截器面试总结)(10)

以上就是字节面试后总结的几个要点,还不会的同学赶紧学起来吧,感谢您的阅读,创造不易,如果您觉得本篇文章对您有帮助,请点击关注小编,您的支持就是小编创作的最大动力!

猜您喜欢: