日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Okhttp3-网络请求流程解析

發(fā)布時間:2024/1/17 编程问答 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Okhttp3-网络请求流程解析 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

前言

已經(jīng)大火2年的Retrofit,必然會提到另外兩個庫,OKhttp3和Rxjava,尤其前者,作為Retrofit網(wǎng)絡(luò)請求的底層庫,我們有必要了解OKhttp3的網(wǎng)絡(luò)請求是如何運作的,就會理解為什么OKhttp3比其它網(wǎng)絡(luò)請求庫更高效,為什么Volley,Glide,Picasso在后續(xù)版本紛紛改用或支持OKhttp3作為自身網(wǎng)絡(luò)傳輸層的底層庫。

基本構(gòu)成

OKhttp3作為Square公司(貢獻了很多優(yōu)秀的開源庫,比如Retrofit,OKhttp,Okio,Picasso等)開發(fā),旨于替換Java的HttpUrlConnection和Apache的HttpClient的輕量級網(wǎng)絡(luò)框架,已經(jīng)被運用到很多開源庫以及Android的源碼中(Android Studio 在6.0之后,移除了HttpClient,并且用OKHttp代替了HttpUrlConnection)。

和其它網(wǎng)絡(luò)框架類似,OKhttp3也主要由以下6個概念一起運作:

1.OkHttpClient:客戶端;

2.Dispatcher:線程池;

3.Interceptor:攔截器(OKhttp的特色);

4.Request:請求;

5.Response:響應(yīng);

6.CallBack:回調(diào)。

Get請求流程

1.創(chuàng)建OkHttpClient客戶端.

2.創(chuàng)建Request請求,設(shè)置url.

3.通過OkHttpClient的newCall()方法,將Request包裝成一個Call接口.

4.最后調(diào)用execute()方法得到同步的響應(yīng)Response.

5.或者調(diào)用execute()方法,以CallBack回調(diào)接口作為參數(shù),得到異步的響應(yīng)Response.

POST請求流程


流程和GET請求類似.

不同的地方在于:

1.需要RequestBody請求體封裝各種類型的請求參數(shù).

2.調(diào)用Request.Builder的post()方法,傳入RequestBody,設(shè)置為POST請求.

接下來,我們對其內(nèi)部的流程進行分析

Get請求內(nèi)部流程分析

第一步:OkHttpClient client = new OkHttpClient();

1.通過OkHttpClient的Builder的默認構(gòu)造方法來初始化網(wǎng)絡(luò)所需的各種成員:

這些成員依次為:

Dispatcher:線程池

Proxy:代理

Protocol:協(xié)議

ConnectionSpec:連接規(guī)則

Interceptor:攔截器

ProxySelector:代理選擇器

CookieJar:Cookie緩存

Cache:緩存

SocketFactory:Socket工廠

HostnameVerifier:主機校隊

SSLSocketFactory/CertificatePinner:SSL證書相關(guān)

ConnectionPool:連接池

等等

2.然后再由Builder將這些成員設(shè)置給OkHttpClient對象:


第二步:Request request = new Request.Builder().url("http://xxxxxx").build();

1.通過Request的Builder的默認構(gòu)造方法,初始化請求部分所需的請求方式method和默認的請求頭headers,



2.再由Builder的url()方法設(shè)置請求的鏈接地址,最后調(diào)用build()方法返回Request對象。


3.在Builder的build()方法中,調(diào)用了Request的構(gòu)造參數(shù),將method,headers等成員設(shè)置給Request對象。

第三步:Call call =client.newCall(request);

1.通過OkhttpClient的newCall()方法,構(gòu)建一個RealCall對象,且其持有第一,二步創(chuàng)建的OkhttpClient和Request對象的引用。

2.同時RealCall對象還會創(chuàng)建一個攔截器RetryAndFollowUpInterceptor。


第四步:

(一)同步請求:Response response =call.execute();

1.調(diào)用第三步的RealCall對象的execute()方法,該方法先檢查該RealCall對象是否已經(jīng)執(zhí)行過該方法了,重復執(zhí)行會拋出異常。




2.調(diào)用第一步的OkhttpClient的dispatcher()方法,獲取到OkhttpClient的線程池Dispatcher,然后執(zhí)行其executed()方法;

runningSyncCalls是Dispatcher中維護的一個正在執(zhí)行的同步請求隊列,RealCall對象會被加入到該隊列的末尾。



3.然后執(zhí)行RealCall對象的getResponseWithInterceptorChain()方法;

按固定順序?qū)r截器(依次為初始化OkhttpClient的攔截器,重試和重定向的攔截器,橋接轉(zhuǎn)換攔截器,緩存攔截器,連接攔截器,初始化OkhttpClient的網(wǎng)絡(luò)攔截器,服務(wù)器回調(diào)攔截器)添加到Interceptors集合中,與第二步的Request一起作為構(gòu)造參數(shù),創(chuàng)建了一個RealInterceptorChain對象。

這里注意構(gòu)造參數(shù)中的(this.index)0,就是RealInterceptorChain的成員變量index,用來標記執(zhí)行到了Interceptors集合中哪一個攔截器的intercept()方法。

4.緊接著執(zhí)行RealInterceptorChain對象的proceed()方法;

首先會作判斷:if(this.index >= this.interceptors.size()) {

throw new AssertionError();

} else { … },這個判斷有什么用呢,先看下面。

因為首次進入httpStream為null,所以不會執(zhí)行同一請求檢查this.sameConnection(request.url())和請求次數(shù)的檢查this.calls >1.

5.接著看以下三句代碼:

RealInterceptorChain next = new RealInterceptorChain(this.interceptors, streamAllocation, httpStream, connection, this.index +1, request);Interceptor interceptor = (Interceptor)this.interceptors.get(this.index);Response response = interceptor.intercept(next);

(1)以Interceptors集合和Request對象,以及index+1作為構(gòu)造參數(shù),創(chuàng)建一個新的RealInterceptorChain對象;

(2)執(zhí)行index位置的攔截器的intercept()方法,同時將新創(chuàng)建的RealInterceptorChain對象傳遞進去,很明顯,這個RealInterceptorChain對象的proceed()方法又會被執(zhí)行,因此,結(jié)合第4點的代碼判斷,通過迭代,Interceptors集合中的所有攔截器都會執(zhí)行intercept()方法:

a.OkhttpClient的interceptors集合,默認是空集;

b.RetryAndFollowUpInterceptor攔截器:負責請求的重試和重定向,最多20次。

c.BridgeInterceptor橋接攔截器:負責請求構(gòu)建和響應(yīng)

d.CacheInterceptor緩存攔截器:負責網(wǎng)絡(luò)緩存操作

e.ConnectInterceptor連接攔截器:負責socket的IO操作,這里使用了Okio提供的封裝


socket操作就是在這個攔截器里執(zhí)行的。


f.OkhttpClient的networkInterceptors攔截器,默認是空集.

g.CallServerInterceptor攔截器:向服務(wù)器發(fā)送請求,將請求header和body寫入socket中,然后讀取響應(yīng)header和body,返回最后需要的響應(yīng)數(shù)據(jù).

下圖是CallServerInterceptor的intercept()方法的實現(xiàn)

(4)最后的CallServerInterceptor攔截器執(zhí)行完intercept()方法后,返回請求的響應(yīng)數(shù)據(jù):Response對象.

服務(wù)器響應(yīng)的數(shù)據(jù)主要通過其中的ResponseBody對象獲取。

5.最后不管請求是否成功,最后都會執(zhí)行Dispatcher的finished()方法,結(jié)束整個請求;

同步請求,不會執(zhí)行Dispatcher的promoteCalls()方法(這個方法在后面的異步請求再分析),通過runningSyncCalls隊列的remove()方法將RealCall從運行隊列中移除.

(二)異步請求:
call.enqueue(new Callback() {
@Override
public void onFailure(Call call,IOException e) {}
@Override
public void onResponse(Call call,Response response) throwsIOException {}
});

與同步請求不同的地方在于

1.執(zhí)行RealCall對象的的enqueue()方法,需要一個CallBack接口實現(xiàn)作為參數(shù),執(zhí)行最后請求的成功和失敗回調(diào);

該方法內(nèi)部是調(diào)用OkhttpClient的Dispatcher的enqueue()方法,同時傳入一個AsyncCall對象作為參數(shù),每個RealCall對象只能執(zhí)行一次。

2.該AsyncCall對象持有第1點中創(chuàng)建的CallBack對象.

3.如果正在執(zhí)行的異步隊列runningAsyncCalls沒有超過最大請求數(shù)(最大為64)并且該請求的主機的最大請求數(shù)沒有超過最大限制(最大為5)時,AsyncCall對象會被加入到runningAsyncCalls中;否則,AsyncCall會被加入到準備執(zhí)行的異步隊列readyAsyncCalls中。

4.如果AsyncCall加入了運行隊列,會通過Dispatcher的executorService()方法,創(chuàng)建一個單例線程池ThreadPoolExecutor。


使用到的ThreadPoolExecutor的構(gòu)造參數(shù):

corePoolSize:并發(fā)數(shù),maximumPoolSize:最小線程數(shù)為0,最大線程數(shù)為Integer.MAX_VALUE;

keepAliveTime:空閑線程的存活時間;

workQueue:先進先出的工作隊列;

threadFactory:單個線程的線程工廠。

緊接著調(diào)用ThreadPoolExecutor的execute()方法,

command就是傳入的AsyncCall,然后執(zhí)行addWorker()方法

在addWorker()方法中,可以發(fā)現(xiàn)firstTask(即上述的AsyncCall),被包裝成Worker后,再由其內(nèi)部的Thread執(zhí)行了start()方法。




5.AsyncCall 繼承自NamedRunnable 而NamedRunnable是Runnable接口的抽象實現(xiàn)。


ThreadPoolExecutor的execute()方法執(zhí)行了工作線程,觸發(fā)了線程內(nèi)部的Runnable(即AsyncCall )的run()方法,run()方法內(nèi)部執(zhí)行AsyncCall的execute()方法。

6.最后執(zhí)行的AsyncCall的execute()方法

調(diào)用RealCall的getResponseWithInterceptorChain()方法獲取最后響應(yīng)的數(shù)據(jù)Response(這一步的內(nèi)部流程和同步請求一樣,不再累述)。

如果中途通過retryAndFollowUpInterceptor攔截器取消了請求,或者拋出IO異常,則請求失敗,回調(diào)responseCallBack(即第1點傳入的CallBack接口實現(xiàn))的onFailure()方法;否則,請求成功,回調(diào)responseCallBack的onResponse()方法。

不管請求失敗還是成功,都會調(diào)用線程池Dispatcher的finished()方法。


異步請求在結(jié)束請求時,傳入的promoteCalls為true,將改請求從正在執(zhí)行的異步隊列中移除后,會額外執(zhí)行promoteCalls()方法,檢查是否有待執(zhí)行的請求。


promoteCalls()方法中,如果正在執(zhí)行的異步隊列的請求數(shù)小于最大請求數(shù),就會繼續(xù)檢查準備執(zhí)行的隊列中是否有還沒有執(zhí)行的請求,如果有,則取出最早存入準備執(zhí)行的隊列的AsyncCall,只要單個主機的請求數(shù)也小于最大請求數(shù),就會重復上述第3點的方法,將這個沒執(zhí)行的請求執(zhí)行下去。

從上述可以總結(jié)這個Dispatcher的特點:

1.Okhttp3的Dispatcher線程池,同步請求為一個工作對列,異步請求時通過一個工作隊列和一個準備隊列來互相配合,支持最大64個的并發(fā)請求,通過Deque隊列先進先出的特點控制請求執(zhí)行的順序,而不是通過鎖機制;

2.整個Dispatcher內(nèi)部只創(chuàng)建一個ThreadPoolExecutor,不保存最小的存活線程數(shù),最大線程數(shù)為Integer.MAX_VALUE,為每個正在執(zhí)行的請求創(chuàng)建一個線程,當線程空閑60s后,結(jié)束線程;

3.設(shè)置有主機數(shù)限制,最大每個主機支持5個請求。

在異步請求中,Dispatcher作為第一個接收請求的對象,根據(jù)當前正在執(zhí)行的請求的狀況,將新的請求指派到工作隊列中并發(fā)處理,或者添加到準備隊列中緩存起來;不限制單例線程池的最大線程數(shù),減少高并發(fā)時額外線程創(chuàng)建的時間耗費,同時不保留最小存活線程數(shù),設(shè)置線程空閑60s后銷毀,避免資源的長期占用;通過try catch finally 塊控制請求隊列的執(zhí)行順序,而沒有使用鎖機制,這幾個地方的設(shè)計都很巧妙。

而Volley的Diapatcher則是由1個緩存線程和默認4個線程的網(wǎng)絡(luò)線程池組成,線程池采用輪詢的機制,這在應(yīng)對高并發(fā)和大數(shù)據(jù)的請求時并不算高效。

POST請求

內(nèi)部流程和GET請求基本一樣,除了在上述第二步生成的Request對象時,需要額外的RequestBody封裝不同類型的請求參數(shù)外。



結(jié)語

Okhttp3目前已經(jīng)有很多通用的第三方封裝框架,但是如果配合Rxjava使用,建議使用Okhttp3的同步請求,自己封裝一層,可以滿足一般項目開發(fā)的基本需求。從上面可以看到,其實源碼并不是那么難懂,尤其是同步請求,只要多看幾遍,即可看到不少Okhttp3的內(nèi)部運作流程和巧妙之處,這也是建議在配合Rxjava使用時,自己封裝一層的原因。當然,如果是配合Retrofit和Rxjava使用,那么就不需要對其過度封裝了,因為Retrofit本身就是對Okhttp3的封裝庫。這次分析流程比較長,還是建議自己寫幾個例子后,逐步去分析,會對其內(nèi)部的流程有一個很明了的認知。

原文發(fā)布時間為:2018-07-09
本文作者:lzt橘子
本文來自云棲社區(qū)合作伙伴“ 安卓巴士Android開發(fā)者門戶”,了解相關(guān)信息可以關(guān)注“ 安卓巴士Android開發(fā)者門戶”。

總結(jié)

以上是生活随笔為你收集整理的Okhttp3-网络请求流程解析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。