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

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

我是不是背叛了你不知不觉,已经忘了你的模样。习惯了自己的姿态,总把一切习以为常。当你饿了,我问你为什么不在饭点去吃饭;当你被屎尿憋得难受,我问你为什么不去厕所;当你病了,我问……ampampquot乒乓小将ampampquot杨惠泽以不败今年5月,南方都市报发文:《深圳两位乒乓小将以不败成绩入选国少集训队》引来众多网友围观。文章的主角之一杨惠泽2010年出生于新疆,后来辗转到深圳南山市读书求学,就读深圳西丽学校……08年从舞台跌落摔成终身残疾,张艺谋愧疚一生的女人还好吗?前序:2008年8月8号,这是我国举办奥运开幕式的日子。在这天,北京鸟巢内,我们向世界展示了一场视觉盛宴。开幕式上,演员们精彩绝伦的表演征服了全世界的观众。……西蒙斯是否把自己的身价打没了?他还有救吗?我认为就是西蒙斯把自己的身价打没有了,只要他能振作起来努力练习还是有优秀的表现。说道西蒙斯必不可少的一个问题就是,他的天赋和詹姆斯差距很小,为什么两人的成绩差距这么大呢?我感觉……摔角新闻2021。12。27WWE迎新大赛最新赔率,大布又要关注我,每天分享摔角最新资讯和视频罗林斯可能成为罗曼下一个对手在与DutchMantell和SPIII进行SmackTalk谈话时,职业摔跤记者BillApter讨……暗影火炬城,我至今玩过最好的国产独立游戏本文作者:苍薪今年的国产游戏不可谓不多,也算是国产独立游戏崛起的一年吧,而有一款游戏确实是我期待已久的,那便是《暗影火炬城》。《暗影火炬城》刚刚发售之时是主机端独占的,虽……女人的美丽是养出来的,坚持6个抗老习惯,越来越美越来越年轻没人永远年轻,可永远有人比你年轻。皮肤保养对我们女人来说十分重要!因为,一个女人皮肤紧致与否,光滑细腻与否、红润有光泽与否都直接决定着你看起来是不是显老。不过,有一些小姐……steam被墙了进不去打不开登不上解决办法汇总话说每年都有关于steam被墙了的谣言,小编在此辟个谣,没有的事,不要当真。万一真遇到进不去、打不开、登不上怎么办?下面是小编收集来的相关的解决办法汇总,一起来看看吧。s……喝酒能杀菌?少喝点就不伤肝?别再被骗啦喝酒是很多人联络感情、抒发情绪的不二选择,朋友聚会来一杯,工作应酬来一杯,有事庆祝来一杯,伤心失意来一杯但是,你知道吗?据2019年全球疾病负担研究显示,喝酒已经成为25……超级人类公测攻略助你更快上手超级人类开启公测啦,这款生存射击游戏在大逃杀玩法的基础上,为游戏角色增加了一些超能力,这一创新之举使游戏可玩性得到进一步提升。比如海军陆战队角色的超能力海雾,可以在打架时放出烟……换脸后好运连连的8大女星杨颖逆袭成顶流,陈小纭咸鱼翻身爱美之心,人皆有之。有些人天生并不是很完美,为了追求完美,很多人不惜花费大价格去整容,只为了让自己看得更好看一些。说起来,娱乐圈中整容的明星不少,但有一类却十分好运……纪念东航事故,飞往天堂飞往天堂方这不是葬礼,这是一场新生。这个飞机,已经启航。爱和恨。留在了人间。能看到的。只有模糊的记忆。它的坐标在广西。旅行的路,很远。它要经历云层,闪电和暴雨。这是……
中国足球愚蠢至极的错误何时休?当中国男足国家队知名球员费尔南多因为中国足协管理工作愚蠢疏忽,在飞机场的长椅上委屈了一晚上,找了个借口返回自己的祖国巴西,居然退出了国家队如此关键的比赛。如此矫情的运动员……苍山下漫步洱海边骑行丨一天看尽世间的美好如果你想要找到一个纯净又浪漫的地方,那就来大理看看吧!这里的天空美团都有不一样的模样只要与美好相关的事情,似乎你都可以在这里找到在这里可以一直从巍峨雪山脚下走……同为发达国家,日韩两国的农村一对比,两者差距有多大?韩国与日本作为我们的邻国,同时受我国历史文化的长远影响,韩日文化与我国有着许多相似之处,因此,韩国和日本一直是我国人民旅游和留学的首选之地。那么韩国和日本同为发达国家又是……盘点2021NBA十大事件!哪些让你印象深刻?2021年科比正式成为名人堂球员;2021年字母哥帮助雄鹿时隔50年再获总冠军;2021年库里成为NBA历史三分第一人;2021年即将结束,这一年的NBA发生……经典再现坚守传承八位堂虎年限量版手柄发布有些平凡的坚守,因为坚持而伟大;有些执着的前行,因为历久而弥新。很多经典就是在坚持和执着中成就的。就像央视春晚、就像罗振宇的跨年演讲、就像新年跨年的钟声,一些固定时段的固定事件……家用空调选用几匹合适?三恒系统帮您远离空调病家用空调选用几匹最合适?房间面积大小不同,适用的空调匹数也不同,家中选择空调其实非常的麻烦,空调买小了,降温速度比不上升温速度,制冷效果差;那就直接往大的买,买匹数大的空……NBA官宣!2022年最佳防守球员正式公布NBA今天官方宣布,凯尔特人后卫马库斯斯马特当选本赛季最佳防守球员。昨天NBA官方公布今年常规赛各大奖项最终候选名单,马库斯斯马特、鲁迪戈贝尔和米卡尔布里奇斯入围最佳防守……中国女排涌现3位主攻新星!张常宁多一个对角,栗垚找到好帮手排超联赛即将进行决赛阶段的比拼,天津女排与江苏女排会师决赛。由于今年是赛事大年,奥运会和全运会同年举行,导致各支队伍的阵容发生巨大变化,许多年轻小将从青年队成功升入到成年队。通……九阴颜值哪家强?来看看这两家颜值巅峰的门派现在还有很多游戏厂商都投身于武侠游戏这一个行列之中,因为武侠确实是一个非常好的游戏主题,并且也深受广大玩家的喜爱,而制作武侠游戏除了表现武侠之外,还有一个要点就是,你的游戏建模……中国女队摘得铜牌中国男队差距明显中国队球员曹君伟、万济圆、王丽丽和张芷婷(从左至右)在赛后庆祝新华社发国际篮联三人篮球世界杯27日在比利时安特卫普落幕,塞尔维亚队和法国队分别夺得男子组和女子组冠军。参赛……06年德国世界杯预选赛,中国队为什么连最后的八强赛都没进?06年德国世界杯亚洲区预选赛最后分为两个阶段,第一阶段是32强赛,每组4队分为8组,小组第一名晋级,参加第二阶段的8强赛。当时的中国队,整体实力并不弱,02年成功进军世界杯,0……美版知乎提问到中国旅游后你对中国的看法有什么改变吗?中国由于经济发展迅速,人们的生活水平也已经有了大幅提高,在以前很多外国人眼中的中国是贫穷落后的,直到现在很多外国人仍然对我们国家抱有一定的偏见,之所以会有这种现象的发生,是因为……
友情链接:快好找快生活快百科快传网中准网文好找聚热点快软网