应用办公生活信息教育商业
投稿投诉
商业财经
汽车智能
教育国际
房产环球
信息数码
热点科技
生活手机
晨报新闻
办公软件
科学动态
应用生物
体育时事

okhttp34。9。3版本简单解析

  一、写在前面
  关于okhttp3的解析网上已经有非常多优秀的博文了,每每看完都觉得醍醐灌顶,豁然开朗。但等不了几天再回头看,还是跟当初一样陌生,究其根本原因,我们不过是在享受着别人的成果跟着别人的思路云阅读源码了一遍。okhttp从早期的Java版本到Kotlin版本一直不断优化升级,实现细节上也作出了调整。重读源码加上自身的思考能深刻地理解okhttp的实现原理。二、从基本使用说起项目中引入依赖,Github地址okhttpdependencies{。。。implementation(com。squareup。okhttp3:okhttp:4。9。3)}从一个简单的同步(execute)请求入手分析整体的加载流程:privatefunclientNetWork(){valrequest:RequestRequest。Builder()。url(https:www。baidu。com)。build()OkHttpClient()。newCall(request)。execute()}
  从execute()开始,发现其实是一个接口中的方法(Call),这个很好理解根据官方的解释,Call其实是一个待执行的请求,并且这个请求所要的参数已经被准备好;当然既然是请求,那么它是可以被取消的。其次代表单个请求与响应流,因此不能够被再次执行。Acallisarequestthathasbeenpreparedforexecution。Acallcanbecanceled。Asthisobjectrepresentsasinglerequestresponsepair(stream),itcannotbeexecutedtwice。
  Call接口具体的代码实现,重点关注同步执行方法execute()与异步请求enqueue():interfaceCall:Cloneable{funrequest():Request同步请求Throws(IOException::class)funexecute():Response起步请求funenqueue(responseCallback:Callback)funcancel()funisExecuted():BooleanfunisCanceled():BooleanfuninterfaceFactory{funnewCall(request:Request):Call}}
  Call作为接口,那么具体的实现细节则需要看它的实现类,RealCall作为Call的实现类,先找到对execute()的重写。RealCall是应用程序与网络之间的桥梁,看看具体有什么作用,首先分析同步请求execute()方法的实现:overridefunexecute():Response{check(executed。compareAndSet(false,true)){AlreadyExecuted}1timeout。enter()2callStart()3try{client。dispatcher。executed(this)4returngetResponseWithInterceptorChain()5}finally{client。dispatcher。finished(this)6}}
  代码很少,逐步分析,首先对同步请求进行了检查判断请求是否已经被执行过了;而这里使用的是并发包下的原子类CAS乐观锁,这里使用CAS比较算法目的也是为提升效率。其次是超时时间的判断,这个比较简单。在看callStart()的具体实现。上代码:privatefuncallStart(){this。callStackTracePlatform。get()。getStackTraceForCloseable(response。body()。close())eventListener。callStart(this)}
  看名称猜测应该是事件监听之类的,可能是包括一些信息的记录与打印。回到RealCall类中,看看这个eventListener的作用到底是什么:
  internalvaleventListener:EventListenerclient。eventListenerFactory。create(this)
  继续向下可以知道这个EventListener是一个抽象类,而项目中其唯一实现类为LoggingEventListener,猜测还是有依据的,继续往下看:abstractclassEventListener{openfuncallStart(call:Call){}省略了其他的方法}
  实现类LoggingEventListener中对此方法的具体实现:classLoggingEventListenerprivateconstructor(privatevallogger:HttpLoggingInterceptor。Logger):EventListener(){overridefuncallStart(call:Call){startNsSystem。nanoTime()logWithTime(callStart:{call。request()})}}
  总结这个callStart()的作用,当同步请求或者异步请求被加到队列时,callStart()会被立即执行(在没有达到线程限制的情况下)记录请求开始的时间与请求的一些信息。如下:overridefuncallStart(call:Call){startNsSystem。nanoTime()logWithTime(callStart:{call。request()})}privatefunlogWithTime(message:String){valtimeMsTimeUnit。NANOSECONDS。toMillis(System。nanoTime()startNs)logger。log(〔timeMsms〕message)}
  代卖第四段4client。dispatcher。executed(this),看样子是在这里开启执行的,可实际真是如此嘛?回到OkHttpClient类中,看看这个分发器dispatcher到底是什么。get:JvmName(dispatcher)valdispatcher:Dispatcherbuilder。dispatcher
  具体实现类Dispatcher代码(保留重要代码):classDispatcherconstructor(){Readyasynccallsintheordertheyllberun。privatevalreadyAsyncCallsArrayDeque()Runningasynchronouscalls。Includescanceledcallsthathaventfinishedyet。privatevalrunningAsyncCallsArrayDeque()Runningsynchronouscalls。Includescanceledcallsthathaventfinishedyet。privatevalrunningSyncCallsArrayDequeRealCall()constructor(executorService:ExecutorService):this(){this。executorServiceOrNullexecutorService}get:SynchronizedvarmaxRequests64set(maxRequests){require(maxRequests1){max1:maxRequests}synchronized(this){fieldmaxRequests}promoteAndExecute()}get:SynchronizedvarmaxRequestsPerHost5set(maxRequestsPerHost){require(maxRequestsPerHost1){max1:maxRequestsPerHost}synchronized(this){fieldmaxRequestsPerHost}promoteAndExecute()}异步请求internalfunenqueue(call:AsyncCall){synchronized(this){readyAsyncCalls。add(call)MutatetheAsyncCallsothatitsharestheAtomicIntegerofanexistingrunningcalltothesamehost。if(!call。call。forWebSocket){valexistingCallfindExistingCallWithHost(call。host)if(existingCall!null)call。reuseCallsPerHostFrom(existingCall)}}promoteAndExecute()}同步请求Usedby〔Call。execute〕tosignalitisinflight。Synchronizedinternalfunexecuted(call:RealCall){runningSyncCalls。add(call)}privatefunpromoteAndExecute():Boolean{this。assertThreadDoesntHoldLock()valexecutableCallsmutableListOf()valisRunning:Booleansynchronized(this){valireadyAsyncCalls。iterator()while(i。hasNext()){valasyncCalli。next()Maxcapacity。if(runningAsyncCalls。sizethis。maxRequests)breakHostmaxcapacity。if(asyncCall。callsPerHost。get()this。maxRequestsPerHost)continuei。remove()asyncCall。callsPerHost。incrementAndGet()executableCalls。add(asyncCall)runningAsyncCalls。add(asyncCall)}isRunningrunningCallsCount()0}for(iin0untilexecutableCalls。size){valasyncCallexecutableCalls〔i〕asyncCall。executeOn(executorService)}returnisRunning}}
  根据注释的信息可以知道,Dispatcher是处理异步请求的执行的策略,当然开发可以实现自己的策略。Policyonwhenasyncrequestsareexecuted。EachdispatcherusesanExecutorServicetoruncallsinternally。Ifyousupplyyourownexecutor,itshouldbeabletoruntheconfiguredmaximumnumberofcallsconcurrently。
  知道了Dispatcher的作用,再回到client。dispatcher。executed(this),也即:Usedby〔Call。execute〕tosignalitisinflight。Synchronizedinternalfunexecuted(call:RealCall){runningSyncCalls。add(call)}
  结合execute()与Dispatcher分析Dispatcher主要是对异步请求的调度作用,内部实现了线程池,对请求进行分发执行。包含三个双端队列分别为等待的异步队列readyAsyncCalls、执行中的队列runningAsyncCalls、执行中的同步队列runningSyncCalls。这里使用ArrayDeque,为什么不使用LinkedList有兴趣的可以思考一下。当请求为同步请求时,内部执行execute()将RealCall添加到runningSyncCalls队列中。
  到这里请求其实还没有真正的执行,只是在做一些前期的工作,回到Call接口中看看对方法同步请求方法execute()的说明:同步请求可以立即执行,阻塞直到返回正确的结果,或者报错结束。Invokestherequestimmediately,andblocksuntiltheresponsecanbeprocessedorisinerror。
  到4步执行后,returngetResponseWithInterceptorChain()5这个方法才是请求一步步推进的核心。也是okhttp网络请求责任链的核心模块。三、链的开启getResponseWithInterceptorChain()getResponseWithInterceptorChain()具体实现Throws(IOException::class)internalfungetResponseWithInterceptorChain():Response{Buildafullstackofinterceptors。valinterceptorsmutableListOfInterceptor()interceptorsclient。interceptorsinterceptorsRetryAndFollowUpInterceptor(client)interceptorsBridgeInterceptor(client。cookieJar)interceptorsCacheInterceptor(client。cache)interceptorsConnectInterceptorif(!forWebSocket){interceptorsclient。networkInterceptors}interceptorsCallServerInterceptor(forWebSocket)valchainRealInterceptorChain(callthis,interceptorsinterceptors,index0,exchangenull,requestoriginalRequest,connectTimeoutMillisclient。connectTimeoutMillis,readTimeoutMillisclient。readTimeoutMillis,writeTimeoutMillisclient。writeTimeoutMillis)varcalledNoMoreExchangesfalsetry{valresponsechain。proceed(originalRequest)if(isCanceled()){response。closeQuietly()throwIOException(Canceled)}returnresponse}catch(e:IOException){calledNoMoreExchangestruethrownoMoreExchanges(e)asThrowable}finally{if(!calledNoMoreExchanges){noMoreExchanges(null)}}}
  分析getResponseWithInterceptorChain()方法之前有必要看看OkHttpClient的构造参数,使用的Builder模式,参数很多,可配置化的东西很多,精简一下主要关注几个参数:classBuilderconstructor(){异步请求任务调度器internalvardispatcher:DispatcherDispatcher()链接池internalvarconnectionPool:ConnectionPoolConnectionPool()自定义拦截器internalvalinterceptors:MutableListInterceptormutableListOf()自定义网络拦截器internalvalnetworkInterceptors:MutableListInterceptormutableListOf()缓存internalvarcache:Cache?null代理internalvarproxy:Proxy?null链接协议internalvarprotocols:ListProtocolDEFAULTPROTOCOLS。。。。。。。}添加自定义拦截器的方法funaddInterceptor(interceptor:Interceptor)apply{interceptorsinterceptor}添加自定义网络拦截器的方法funaddNetworkInterceptor(interceptor:Interceptor)apply{networkInterceptorsinterceptor}
  到这里有个疑问,这个添加自定义拦截器与添加自定义网络拦截器有什么区别呢?方法看上去是差不多的,查看官方的说明可以发现一些细节。文档中解释了ApplicationInterceptor与NetworkInterceptors的细微差别。先回到RealCall中查看getResponseWithInterceptorChain()是如何对拦截器结合组装的:internalfungetResponseWithInterceptorChain():Response{Buildafullstackofinterceptors。valinterceptorsmutableListOfInterceptor()interceptorsclient。interceptors1interceptorsRetryAndFollowUpInterceptor(client)interceptorsBridgeInterceptor(client。cookieJar)interceptorsCacheInterceptor(client。cache)interceptorsConnectInterceptorif(!forWebSocket){interceptorsclient。networkInterceptors2}interceptorsCallServerInterceptor(forWebSocket)}
  看1与2分别对应添加的自定义拦截器与自定义网络拦截器的位置,自定义拦截器是拦截器链的链头,而自定义网络拦截器在ConnectInterceptor拦截器与CallServerInterceptor拦截器之间被添加。总结一下:自定义拦截器在整个拦截器链的头部,做一些请求的之前的准备工作,包括一些Log的信息打印,如LoggingInterceptor。自定义网络拦截器添加在ConnectInterceptor与CallServerInterceptor之间,可以执行两次,并且获得的信息更多,包括原网址的信息和重定向后网址的信息。Applicationinterceptors
  Don’tneedtoworryaboutintermediateresponseslikeredirectsandretries。
  Arealwaysinvokedonce,eveniftheHTTPresponseisservedfromthecache。
  Observetheapplication’soriginalintent。UnconcernedwithOkHttpinjectedheaderslikeIfNoneMatch。
  PermittedtoshortcircuitandnotcallChain。proceed()。
  PermittedtoretryandmakemultiplecallstoChain。proceed()。
  CanadjustCalltimeoutsusingwithConnectTimeout,withReadTimeout,withWriteTimeout。NetworkInterceptors
  Abletooperateonintermediateresponseslikeredirectsandretries。
  Notinvokedforcachedresponsesthatshortcircuitthenetwork。
  Observethedatajustasitwillbetransmittedoverthenetwork。
  AccesstotheConnectionthatcarriestherequest。
  综上可以得出整个链的顺序结构,如果都包含自定义拦截器与自定义网络拦截器,则为自定义拦截器RetryAndFollowUpInterceptorBridgeInterceptorCacheInterceptorConnectInterceptor自定义网络拦截器CallServerInterceptor;那么链是如何按照顺序依次执行的呢?okhttp在这里设计比较精妙,在构造RealInterceptorChain对象时带入index信息,这个index记录的就是单个拦截器链的位置信息,而RealInterceptorChain。proceed(request:Request)通过index自增一步步执行责任链一直到链尾。valchainRealInterceptorChain(callthis,interceptorsinterceptors,记录了拦截器对应的index通过对index0,exchangenull,requestoriginalRequest,connectTimeoutMillisclient。connectTimeoutMillis,readTimeoutMillisclient。readTimeoutMillis,writeTimeoutMillisclient。writeTimeoutMillis)valresponsechain。proceed(originalRequest)四、proceed链的推进RealInterceptorChain中方法proceed()方法的具体实现Throws(IOException::class)overridefunproceed(request:Request):Response{check(indexinterceptors。size)callsif(exchange!null){check(exchange。finder。sameHostAndPort(request。url)){networkinterceptor{interceptors〔index1〕}mustretainthesamehostandport}check(calls1){networkinterceptor{interceptors〔index1〕}mustcallproceed()exactlyonce}}Callthenextinterceptorinthechain。valnextcopy(indexindex1,requestrequest)valinterceptorinterceptors〔index〕Suppress(USELESSELVIS)valresponseinterceptor。intercept(next)?:throwNullPointerException(interceptorinterceptorreturnednull)。。。。。returnresponse}
  简单的分析推进过程:
  1。RealInterceptorChain的构造参数中携带了index的信息,index自增通过proceed方法不断执行。
  2。拦截器统一实现Interceptor接口,接口中funproceed(request:Request):Response保证了链式链接。当然拦截器的顺序是按照一定的规则排列的,逐个分析。五、RetryAndFollowUpInterceptor重试与重定向companionobject{privateconstvalMAXFOLLOWUPS20}overridefunintercept(chain:Interceptor。Chain):Response{valrealChainchainasRealInterceptorChainvarrequestchain。requestvalcallrealChain。callvarfollowUpCount0varpriorResponse:Response?nullvarnewExchangeFindertruevarrecoveredFailureslistOfIOException()while(true){。。。。try{responserealChain。proceed(request)newExchangeFindertrue}}。。。。}
  1。重试拦截器规定默认的重试次数为20次
  2。以responserealChain。proceed(request)为分界点,包括其他的拦截器,在责任链传递之前所做的工作都是前序工作,然后将request下发到下一个拦截器。
  3。responserealChain。proceed(request)后的代码逻辑为后续工作,即拿到上个拦截器的response结果,有点递归的意思,按照责任链的执行一直到最后一个拦截器获得的结果依次上抛每个拦截器处理这个response完成一些后序工作。
  4。当然并不是每个请求都会走完整个链,如CacheInterceptor当开启了缓存(存在缓存)拿到了缓存的response那么之后的拦截器就不会在继续传递。六、BridgeInterceptoroverridefunintercept(chain:Interceptor。Chain):Response{valuserRequestchain。request()valrequestBuilderuserRequest。newBuilder()valbodyuserRequest。bodyif(body!null){valcontentTypebody。contentType()if(contentType!null){requestBuilder。header(ContentType,contentType。toString())}valcontentLengthbody。contentLength()if(contentLength!1L){requestBuilder。header(ContentLength,contentLength。toString())requestBuilder。removeHeader(TransferEncoding)}else{requestBuilder。header(TransferEncoding,chunked)requestBuilder。removeHeader(ContentLength)}}。。。。。分界点,上部分为前序操作valnetworkResponsechain。proceed(requestBuilder。build())下部分代码为拿到response的后序操作cookieJar。receiveHeaders(userRequest。url,networkResponse。headers)}
  1。桥接拦截器主要对请求的Hader的信息的补充,包括内容长度等。
  2。传递请求到下一个链,等待返回的response信息。
  3。后序的操作包括Cookie、gzip压缩信息,UserAgent等信息的补充。七、CacheInterceptoroverridefunintercept(chain:Interceptor。Chain):Response{valcallchain。call()valcacheCandidatecache?。get(chain。request())valnowSystem。currentTimeMillis()valstrategyCacheStrategy。Factory(now,chain。request(),cacheCandidate)。compute()valnetworkRequeststrategy。networkRequestvalcacheResponsestrategy。cacheResponse。。。。。。varnetworkResponse:Response?nulltry{networkResponsechain。proceed(networkRequest)}finally{IfwerecrashingonIOorotherwise,dontleakthecachebody。if(networkResponsenullcacheCandidate!null){cacheCandidate。body?。closeQuietly()}}。。。。。}OkHttpClient的默认构造中是没有开启缓存的;internalvarcache:Cache?null,需要手动添加,但是有默认实现,基于最近最少使用算法实现的DiskLruCache磁盘缓存:privatevalclient:OkHttpClientOkHttpClient。Builder()。cache(Cache(directoryFile(application。cacheDir,httpcache),0。05worthofphonestoragein2020maxSize50L1024L1024L50MiB))。build()
  1。缓存拦截器默认没有被开启,需要在调用时指定缓存的目录,内部基于DiskLruCache实现了磁盘缓存。
  2。当缓存开启,且命中缓存,那么链的调用不会再继续向下传递(此时已经拿到了response)直接进行后序的操作。
  3。如果未命中,则会继续传递到下一个链也即是ConnectInterceptor。八、ConnectInterceptorOpensaconnectiontothetargetserverandproceedstothenextinterceptor。Thenetworkmightbeusedforthereturnedresponse,ortovalidateacachedresponsewithaconditionalGET。objectConnectInterceptor:Interceptor{Throws(IOException::class)overridefunintercept(chain:Interceptor。Chain):Response{valrealChainchainasRealInterceptorChainvalexchangerealChain。call。initExchange(chain)valconnectedChainrealChain。copy(exchangeexchange)returnconnectedChain。proceed(realChain。request)}}ConnectInterceptor的代码量很少主要作用很清晰。
  1。建立与目标的服务器的TCP或者TCPTLS的链接。
  2。与之前的拦截器不同,前面的拦截器的前序操作基于调用方法realChain。proceed()之前,但是ConnectInterceptor没有后序操作,下发到下一个拦截器。九、CallServerInterceptorThrows(IOException::class)overridefunintercept(chain:Interceptor。Chain):Response{valrealChainchainasRealInterceptorChainvalexchangerealChain。exchange!!valrequestrealChain。requestvalrequestBodyrequest。bodyvalsentRequestMillisSystem。currentTimeMillis()exchange。writeRequestHeaders(request)varinvokeStartEventtruevarresponseBuilder:Response。Builder?nullif(HttpMethod。permitsRequestBody(request。method)requestBody!null){if(100continue。equals(request。header(Expect),ignoreCasetrue)){exchange。flushRequest()responseBuilderexchange。readResponseHeaders(expectContinuetrue)exchange。responseHeadersStart()invokeStartEventfalse}if(responseBuildernull){if(requestBody。isDuplex()){exchange。flushRequest()valbufferedRequestBodyexchange。createRequestBody(request,true)。buffer()requestBody。writeTo(bufferedRequestBody)}else{valbufferedRequestBodyexchange。createRequestBody(request,false)。buffer()requestBody。writeTo(bufferedRequestBody)bufferedRequestBody。close()}}else{exchange。noRequestBody()if(!exchange。connection。isMultiplexed){exchange。noNewExchangesOnConnection()}}}else{exchange。noRequestBody()}if(requestBodynull!requestBody。isDuplex()){exchange。finishRequest()}if(responseBuildernull){responseBuilderexchange。readResponseHeaders(expectContinuefalse)!!if(invokeStartEvent){exchange。responseHeadersStart()invokeStartEventfalse}}varresponseresponseBuilder。request(request)。handshake(exchange。connection。handshake())。sentRequestAtMillis(sentRequestMillis)。receivedResponseAtMillis(System。currentTimeMillis())。build()varcoderesponse。codeif(code100){responseBuilderexchange。readResponseHeaders(expectContinuefalse)!!if(invokeStartEvent){exchange。responseHeadersStart()}responseresponseBuilder。request(request)。handshake(exchange。connection。handshake())。sentRequestAtMillis(sentRequestMillis)。receivedResponseAtMillis(System。currentTimeMillis())。build()coderesponse。code}exchange。responseHeadersEnd(response)responseif(forWebSocketcode101){response。newBuilder()。body(EMPTYRESPONSE)。build()}else{response。newBuilder()。body(exchange。openResponseBody(response))。build()}if(close。equals(response。request。header(Connection),ignoreCasetrue)close。equals(response。header(Connection),ignoreCasetrue)){exchange。noNewExchangesOnConnection()}if((code204code205)response。body?。contentLength()?:1L0L){throwProtocolException(HTTPcodehadnonzeroContentLength:{response。body?。contentLength()})}returnresponse}}CallServerInterceptor是整个责任链的尾链:
  1。实质上是请求与IO操作,将请求的数据写入到Socket中。
  2。从Socket读取响应的数据TCPTCPTLS对应的端口,对于IO操作基于的是okio,而okhttp的高效请求同样离不开okio的支持。
  3。拿到数据reponse返回到之前包含有后序操作的拦截器,但ConnectInterceptor除外,ConnectInterceptor是没有后续操作的。
  整个拦截器流程图如下:
  十、ReaclCall。execute()回头看同步请求的方法:overridefunexecute():Response{check(executed。compareAndSet(false,true)){AlreadyExecuted}timeout。enter()callStart()try{client。dispatcher。executed(this)returngetResponseWithInterceptorChain()}finally{client。dispatcher。finished(this)}}在Dispatcher中对于同步的请求仅仅只是将,RealCall的实例添加到runningSyncCalls。add(call)执行中的队列中。RealCall是Call的唯一实现类,而对于execute()方法的定义明确了,立即执行并阻塞直到response结果的返回或者error信息打断阻塞。经过getResponseWithInterceptorChain()的调用,链的推进此时已经拿到结果,那么后序的操作是什么呢?先看一个Java基础try{}finally{}结构,以及dispatcher。finished(call:RealCall)。try{client。dispatcher。executed(this)returngetResponseWithInterceptorChain()}finally{client。dispatcher。finished(this)}Usedby〔Call。execute〕tosignalcompletion。internalfunfinished(call:RealCall){finished(runningSyncCalls,call)}
  1。排除极端的情况,System。exit()或者其他,finally块必然执行,不论发生异常与否,也不论在finally之前是否有return。
  2。不管在try块中是否包含return,finally块总是在return之前执行。
  3。如果finally块中有return,那么try块和catch块中的return就没有执行机会了。
  Tip:第二条的结论很重要,回到execute()方法,dispatcher。finished(this)在结果response结果返回之前执行,看finished()具体实现。Usedby〔Call。execute〕tosignalcompletion。internalfunfinished(call:RealCall){finished(runningSyncCalls,call)}privatefunTfinished(calls:DequeT,call:T){validleCallback:Runnable?synchronized(this){1if(!calls。remove(call))throwAssertionError(Callwasntinflight!)idleCallbackthis。idleCallback}2valisRunningpromoteAndExecute()if(!isRunningidleCallback!null){idleCallback。run()}}privatefunpromoteAndExecute():Boolean{this。assertThreadDoesntHoldLock()valexecutableCallsmutableListOf()valisRunning:Booleansynchronized(this){3valireadyAsyncCalls。iterator()while(i。hasNext()){valasyncCalli。next()Maxcapacity。if(runningAsyncCalls。sizethis。maxRequests)breakHostmaxcapacity。if(asyncCall。callsPerHost。get()this。maxRequestsPerHost)continuei。remove()asyncCall。callsPerHost。incrementAndGet()executableCalls。add(asyncCall)runningAsyncCalls。add(asyncCall)}4isRunningrunningCallsCount()0}for(iin0untilexecutableCalls。size){valasyncCallexecutableCalls〔i〕asyncCall。executeOn(executorService)}5returnisRunning}SynchronizedfunrunningCallsCount():IntrunningAsyncCalls。sizerunningSyncCalls。size
  1。1方法一,calls。remove(call)返回为true,也即是这个同步请求被从runningSyncCalls中移除释放;所以idleCallback为空。
  2。3很显然asyncCall的结果为空,没有异步请求,在看4具体实现,runningSyncCalls的size为1。则isRunning的结果为true。idleCallback。run()不会被执行,并且idleCallback其实也是为空。至此,开始的例子所构建的同步请求就被执行完毕了,阻塞拿到了结果回调,用于页面展示或者其他操作。梳理整个流程如下:
  1。从OkHttpClient()。newCall(request)。execute()开启同步请求任务。
  2。得到的RealCall对象作为Call的唯一实现类,其中同步方法execute()是阻塞的,调用到会立即执行阻塞到有结果返回,或者发生错误error被打断阻塞。
  3。RealCall中同步execute()请求方法被执行,而此时OkHttpClient实例中的异步任务分发器Dispatcher会将请求的实例RealCall添加到双端队列runningSyncCalls中去。
  4。通过RealCall中的方法getResponseWithInterceptorChain()开启请求拦截器的责任链,将请求逐一下发,通过持有index并自增操作,其次除ConnectInterceptor与链尾CallServerInterceptor其余默认拦截器均有以chain。proceed(request)为分界点的前序与后序操作,拿到response后依次处理后序操作。
  5。最终返回结果response之前,对进行中的同步任务做了移除队列的操作也即finally中client。dispatcher。finished(this)方法,最终得到的结果response返回到客户端,至此整个同步请求流程就结束了。十一、异步请求的处理enqueue()异步请求的大致流程与同步请求是差不多的,但是多了一个从等待队列到执行队列转化的一个过程。internalfunenqueue(call:AsyncCall){synchronized(this){1readyAsyncCalls。add(call)MutatetheAsyncCallsothatitsharestheAtomicIntegerofanexistingrunningcalltothesamehost。if(!call。call。forWebSocket){valexistingCallfindExistingCallWithHost(call。host)if(existingCall!null)call。reuseCallsPerHostFrom(existingCall)}}2promoteAndExecute()}privatefunpromoteAndExecute():Boolean{this。assertThreadDoesntHoldLock()valexecutableCallsmutableListOf()valisRunning:Booleansynchronized(this){valireadyAsyncCalls。iterator()while(i。hasNext()){3valasyncCalli。next()Maxcapacity。if(runningAsyncCalls。sizethis。maxRequests)breakHostmaxcapacity。if(asyncCall。callsPerHost。get()this。maxRequestsPerHost)continue4i。remove()asyncCall。callsPerHost。incrementAndGet()executableCalls。add(asyncCall)5runningAsyncCalls。add(asyncCall)}isRunningrunningCallsCount()0}for(iin0untilexecutableCalls。size){valasyncCallexecutableCalls〔i〕asyncCall。executeOn(executorService)}returnisRunning}1方法中将异步请求任务加入到readyAsyncCalls等待队列之中,2开启执行任务(当然Dispatcher维护了线程池来处理这些请求)。执行方法promoteAndExecute()中3将异步任务从等待队列readyAsyncCalls中取出并移除操作,5则添加到执行中的异步任务队列runningAsyncCalls,如此直到所有请求全部执行完毕。十二、小结这里仅仅是对大概的流程简单的作出了分析,忽略了一些其他的实现细节,像请求缓存池、请求的协议。包括本地缓存DiskLruCache的实现细节,以及异步请求的一些阀值的判断处理。在精力允许的情况下还应该仔细研究一番,毕竟从Java到现在的Kotlin版本okhttp经历了很多的迭代也更加成熟,当然网络请求这块okhttp的地位还是无法撼动的。十三、文档
  Github
  Square

截至目前,盘点2022年搭载5000万像素大底主摄的手机【1】三星GalaxyS22搭载全新一代骁龙8旗舰处理器,采用三星4nm制程工艺,拥有卓越的性能,但功耗也高,发热非常严重正面是一块6。1英寸的三星DynamicA……卡塔尔世界杯开幕式遭吐槽主题曲毫无激情,现场观众均未戴口罩四年一度的卡塔尔世界杯终于正式开幕,此前有关于这一次世界杯的诸多消息都是偏向于夸赞卡塔尔,作为东道主的他们着实是给21支国家队准备了很好的居住和比赛环境。不过在万众期待的开幕式……四问岚图A轮融资50亿元,吸金能力从何而来?中国新能源汽车行业发展至今的最大规模首轮融资终于出现了。11月18日,岚图汽车宣布完成A轮引战融资协议签署与交割,共融资近50亿元,融资后的市场估值接近300亿元。在堪称资本寒……微信新增删除声音锁功能,还有扩展文字输入等功能近日,微信发布了iOS微信8。0。30正式版更新。到目前为止,安卓和苹果机型均更新至8。0。30。微信此次更新新增了删除声音锁功能以及朋友圈扩展文字输入区域等功能。1。微……情欲的声音很温柔人的心里常常有两个声音左右我们的判断,这两个声音的意见完全不一样,一个倾向于邪恶,一个倾向于圣洁。它们就像拔河比赛的双方,而我们则处在中间,被这两个声音使劲地拉拽。……10月自主品牌市场份额超过50,新能源汽车与燃油车分化明显记者周姝祺编辑11月9日,乘联会公布的最新数据显示,2022年10月乘用车市场零售达到184。0万辆,同比增长7。3,,但环比下降4。3,这也是自2013年以来首次……女篮国手23分7篮板7助攻!四川队上演大逆转辽宁名帅带队夺冠都说四川女篮是新晋的土豪球队,可她们还没有拿到过全国冠军。这个夏天,为了能够继续保持冲击冠军,四川女篮邀请辽宁名帅王桂芝执教,虽然都说阵容豪华,但参加全国俱乐部锦标赛的阵容中还……探索原子内部的奥秘,你从来没有真正接触过任何东西在我们的日常生活中,见到过很多微小的东西,比如说头发丝,非常纤细。但你知道吗,即便是一根头发丝的直径,也相当于30万个原子的宽度!如果按比例来放大,把原子放大到肉眼可见的……人到中年光棍的思想三十而立,而立之年,心中的踌躇满志已然烟消云散。渐渐地学会接受生活,学会了躺平。做了一个不求上进的人。请大家一定要以我为反面教材,认真规划自己的人生。1991年羊年出生的……中国男篮大名单公布周琦缺席胡明轩在列中国男篮将在15日0点与巴林男篮进行世预赛第五个窗口期的比赛。目前中国男篮稳居小组第二位置,有较大概率获得2023年世界杯的参赛资格。中国男篮在14日公布中巴之战的大名单,其中……用几块钱就能祛除AJ鞋头褶皱的办法,你不学一下吗?各位喜欢买鞋的小伙伴们应该了解,新鞋到手最怕出现的问题就是:鞋子没穿几天就产生褶皱,或者保存不当就会氧化、发霉等问题了!不过小编发现,在得物社区就有达人分享了球鞋除皱的小……2K价位的屏幕也有越级体验?OPPOA1Pro超高频PWM调双十一后的首场手机发布会让绿厂捷足先登了,带来的新品OPPOA1Pro可以说是亮点频出。从新机的命名来看,该机的确和以往的OPPOA系列有点不一样,很多小伙伴都想知道它究竟有什……
洪金宝在娱乐圈里是不是比成龙还高?根本不是一个级别的!说几个例子。成龙要去参观前中国女首富陈丽华紫檀博物馆,陈丽华亲自接见。成龙去全世界任何地方,都有人认识,包括整个非洲,都是人山人海。……一夜资讯独行侠更衣室内讧,恩比德喜迎一改变,名宿劝诫西蒙斯北京时间4月3日,NBA晚间发生了一些非常有意思的事,现整理总结如下:猛龙狂屠黄蜂,范乔丹砍下双20,一数据比肩哈登,自由市场仍追求顶薪。独行侠爆发激烈内讧,库板无奈恐目送欧文……藿香正气水倒在马桶里,真是厉害了,学会我也要多买几盒藿香正气水作为一种中药材,被广泛应用于中药制剂中,具有清热解毒、疏风散寒、宣肺定喘等功效,尤其在秋冬季节,常被用来预防和治疗感冒、咳嗽等症状。但是,你可能不知道,藿香正气水还有……简单纯粹美好向往的生活任何一个傻瓜都能把事情变复杂,而让事情变得简单则需要天才。E。F舒马赫荐书:《美而简:生活的艺术》作者:【英】萨提斯库马尔哲学家、教育家、生态文明专家和环保运动先驱……细腻胶片颗粒的时尚人像沐浴阳光中的性感身姿LachlanBailey是一位时尚和人像摄影师,目前居住在纽约市。Lachlan最初来自澳大利亚,在那里学习电影和摄影,他于2001年移居伦敦,然后于2008年移居纽约。在协……2023啤酒热度榜(品牌)TOP10重磅发布青岛啤酒夺魁燕京数据来源:灼识咨询数据平台3月9日,每日经济新闻与专业咨询机构CIC灼识咨询(ChinaInsightsConsultancy)联合发布2023啤酒品牌榜(品牌)TOP1……完胜!快船六虎10424,德罗赞215,威少立大功,西部格局北京时间3月28日,NBA常规赛快船主场迎战公牛,赛前,快船39胜36负排名西部第5,而公牛36胜38负位居东部第10,今天太阳也赢球,所以此役快船不容有失,因为从目前西部的局……今晨财经必读(2023。03。25)早新闻大事一览丨要闻丨1、24日,国防部新闻发言人谭克非就美舰擅闯中国西沙领海发表谈话,严正要求美方立即停止此类挑衅行径,否则将承担由此引发不测事件的严重后果。……免费入园!为期一个多月!穿汉服还可免费体验4月1日5月4日倒盏村首届汉风唐韵花朝节在洛阳市伊滨区倒盏村举办超多精彩活动不容错过先码再逛!01汉服游园会人间芳菲四月天共赴花朝好时……华为P60系列支持数字人民币无电支付没电没网也不怕【TechWeb】日前,华为在上海召开了期待已久的新品发布会,全新的华为P60系列和MateX3两大旗舰正式登场。其中华为P60系列首发双向卫星通信、夜视长焦,首发搭载全新鸿蒙……一败涂地!梅西未能兑现承诺,法甲官方被打脸,拜仁不够专业随着欧冠18决赛次回合,舒波莫廷打破僵局,格纳布里替补破门,拜仁主场20战胜大巴黎,这样他们双杀对手,晋级欧冠八强。虽然坐拥梅西和姆巴佩两大顶级巨星,但可惜大巴黎还是无法逃脱连……去年通航飞行量创历史新高,政协委员却指出这些痛点相比于亏损创新高的民航运输业,2022年的全球通航业可以说是已走出疫情阴霾,飞机交付总量超过疫情前。中国的通航飞行量也在2022年创历史新高,通航飞机注册数量较上年增长5……
友情链接:快好找快生活快百科快传网中准网文好找聚热点快软网