OkHttp( 3.9.0-SNAPSHOT)源碼解析

OkHttp源碼的samples的簡單使用的示例:

public static void main(String... args) throws Exception{

OkHttpClient client=new OkHttpClient();

// Create request for remote resource.

Request request=new Request.Builder()

.url(ENDPOINT)

.build();

// Execute the request and retrieve the response.

Response response=client.newCall(request).execute();

// Deserialize HTTP response to concrete type.

ResponseBody body=response.body();

Listcontributors=CONTRIBUTORS_JSON_ADAPTER.fromJson(body.source());

body.close();

......

}

靜態(tài)常量

OkHttpClient開始就定義了兩個靜態(tài)常量激况,這兩個常量是與協(xié)議相關(guān)分別是DEFAULT_CONNECTION_SPECS和DEFAULT_PROTOCOLS呼巷。

靜態(tài)代碼塊

接下來看OkHttpClient的static代碼塊主要就是創(chuàng)建了一個Internal的內(nèi)部類對象。

接口

OkHttpClient實(shí)現(xiàn)了三個接口什往,分別是Cloneable,Call.Factory,WebSocket.Factory

重點(diǎn)在Call.Factory接口上,Call該接口的實(shí)例就是我么去執(zhí)行請求網(wǎng)絡(luò)數(shù)據(jù)的對象尊浓,

Call中的主要方法

/** 返回該Call創(chuàng)建的Request請求對象 */

Request request();

/** 同步執(zhí)行請求網(wǎng)絡(luò)數(shù)據(jù)斩箫,返回服務(wù)端響應(yīng)結(jié)果*/

Response execute()throwsIOException;

/** 異步執(zhí)行請求網(wǎng)絡(luò)數(shù)據(jù)的回調(diào)方法*/

void enqueue(Callback responseCallback);

/** 取消網(wǎng)絡(luò)請求*/

void cancel();

/** 判斷網(wǎng)絡(luò)請求是否被執(zhí)行了*/

boolean isExecuted();

/** 判斷網(wǎng)絡(luò)請求是否被取消了*/

boolean isCanceled();

/**clone該方法是Cloneable接口中的方法,意義在于創(chuàng)建一個相同的請求對象*/

Call clone();

/**Factory內(nèi)部接口就是創(chuàng)建Call的工廠接口,該接口定義的方法newCall需要傳入Request對象*/

interface Factory{

Call newCall(Request request);

}

成員變量

最重要的就是OkHttpClient城瞎,該類的成員變量中6個成員變量進(jìn)行重點(diǎn)說明的

final Dispatcher dispatcher;

final List?interceptors;

final List?networkInterceptors;

final EventListener.Factory eventListenerFactory;

final @Nullable Cache cache;?

final @Nullable InternalCache internalCache;

1、同步請求過程解析

Response response=client.newCall(request).execute(); 這句代碼執(zhí)行過程:

(1)client.newCall(request)

第一步:上面提到OkHttpClient的三個接口中有個Factory內(nèi)部接口(創(chuàng)建Call的工廠接口),而Call對象有個實(shí)現(xiàn)類RealCall疾瓮,其實(shí)newCall方法是RealCall執(zhí)行自己的newRealCall方法創(chuàng)建了一個RealCall實(shí)例脖镀,該方法三個參數(shù)如下:

*@param client OkHttpClient對象

*@param originalRequest 之前傳入的那個原始請求對象

*@param forWebSocket 是否是WebSocket

RealCall call=newRealCall(client,originalRequest,forWebSocket);

第二步:上面提到OkHttpClient的6個成員變量中有個EventListener.Factory eventListenerFactory對象(也是個工廠接口,內(nèi)部定義了一個create方法用于創(chuàng)建EventListener對象)狼电,上一步傳入了OkHttpClient對象蜒灰,該對象已經(jīng)創(chuàng)建完了eventListenerFactory實(shí)例,所以拿到該實(shí)例傳入call對象創(chuàng)建EventListener對象。

抽象類EventListener中有一大堆的網(wǎng)絡(luò)連接的監(jiān)聽方法

call.eventListener=client.eventListenerFactory().create(call);

最后返回當(dāng)前的Call對象

(2)call.execute()

@Override

publicResponse execute()throwsIOException{

? ? synchronized(this) {

? ? if(executed)throw newIllegalStateException("Already Executed");

? ? ?executed=true;

}

? ?captureCallStackTrace();

?try{

? ? ? ?client.dispatcher().executed(this);

? ? ? Response result=getResponseWithInterceptorChain();

? ? ? if(result==null)throw newIOException("Canceled");

? ? ? ?returnresult;

? ? ? }finally{

? ? ? ?client.dispatcher().finished(this);

? ?}

}

2.1)call中的execute方法中的client.dispatcher().executed(this);

由于execute是同步執(zhí)行網(wǎng)絡(luò)請求所以要用關(guān)鍵字synchronized,

synchronized(this) {

? ? if(executed)throw newIllegalStateException("Already Executed");

? ?executed=true;

}

之后再執(zhí)行captureCallStackTrace()方法碰逸,放入堆棧進(jìn)行追蹤捕捉。

client.dispatcher()是獲得上面OkHttpClient的成員變量Dispatcher dispatcher毕骡,注意啦Dispatcher中的方法基本都是同步,用的Synchronized修飾的方法岩瘦。為了分析清楚這里插一段Dispatcher的說明。

Dispatcher中維護(hù)著執(zhí)行發(fā)送請求的線程池窿撬,所有的請求都是放在請求隊列中的總共有三個隊列readyAsyncCalls(準(zhǔn)備的異步請求隊列)启昧,runningAsyncCalls(正在在執(zhí)行的異步請求還包括被中斷的),runningSyncCalls(正在在執(zhí)行的異步請求還包括被中斷的)

private final Deque<AsyncCall> readyAsyncCalls=newArrayDeque<>();

private final Deque<AsyncCall> runningAsyncCalls=newArrayDeque<>();

private final Deque<RealCall> runningSyncCalls=newArrayDeque<>();

RealCall和AsyncCall的關(guān)系通過Dispatcher類的導(dǎo)入代碼可以看出

import okhttp3.RealCall.AsyncCall; ? // AsyncCall其實(shí)就是RealCall的內(nèi)部類

具體實(shí)現(xiàn)如下:

final class AsyncCall extends NamedRunnable{

private final Callback responseCallback;

// 構(gòu)造時需要傳入一個Callback接口劈伴,也就是返回給我們成功失敗的回調(diào)方法

AsyncCall(Callback responseCallback) { ?

? ?super("OkHttp %s",redactedUrl());

? ?this.responseCallback=responseCallback;

}

String host() {

? ? return originalRequest.url().host();

}

Request request() {

? ? ?return originalRequest;

}

RealCall ?get() {

? ? return RealCall.this;

}

@Override

protected void execute() {

? ? ? ? ? boolean signalledCallback=false;

? ?try{

? ? ? ? ? Response response=getResponseWithInterceptorChain();

? ? ? ? ? if(retryAndFollowUpInterceptor.isCanceled()) {

? ? ? ? ? signalledCallback=true;

? ? ? ? ? responseCallback.onFailure(RealCall.this,newIOException("Canceled"));

? ? ? }else{

? ? ? ? ? signalledCallback=true;

? ? ? ? ? responseCallback.onResponse(RealCall.this,response);

? ? ?}

? }catch(IOException e) {

? ? ? ?if(signalledCallback) {

? ? ? ?// Do not signal the callback twice!

? ? ? ? Platform.get().log(INFO,"Callback failure for "+toLoggableString(),e);

? ? }else{

? ? ? ?responseCallback.onFailure(RealCall.this,e);

? ?}

? ? ? }finally{

? ? ? ? client.dispatcher().finished(this);

? ? ? }

? ?}

}

dispatcher中erexecuted方法的具體實(shí)現(xiàn)

// ?加入同步執(zhí)行隊列

synchronized void executed(RealCall call) {

? runningSyncCalls.add(call);

}

2.2)Response result=getResponseWithInterceptorChain();

下面回過頭去看RealCall的execute方法中client.dispatcher().executed(this);執(zhí)行結(jié)束后的代碼Response result=getResponseWithInterceptorChain();

為了分析清楚這里插一段Interceptor的說明

Response getResponseWithInterceptorChain()throwsIOException{

//創(chuàng)建一個存放攔截器的集合密末,最后傳入 newRealInterceptorChain對象中

? ?List<Intercepter> ?interceptors=newArrayList<>();

// client.interceptors() (自定義的攔截器, 在call api的前的攔截) - > //retryAndFollowUpInterceptor (實(shí)現(xiàn)請求重試)

? ?interceptors.addAll(client.interceptors());

// 重定向攔截器

? ?interceptors.add(retryAndFollowUpInterceptor);

// 橋接攔截器(處理header 、cookie 等)

? ?interceptors.add(newBridgeInterceptor(client.cookieJar()));

// 緩存攔截器(處理 cache)

? ?interceptors.add(newCacheInterceptor(client.internalCache()));

// 連接攔截器(負(fù)責(zé)建立連接)

? ?interceptors.add(newConnectInterceptor(client));

? ?if(!forWebSocket) {

// 自定義網(wǎng)絡(luò)攔截器(此時已建立連接)

? ? ?interceptors.addAll(client.networkInterceptors());

? }

// 服務(wù)請求攔截器(發(fā)起請求跛璧、接收響應(yīng))

? ?interceptors.add(newCallServerInterceptor(forWebSocket));

Interceptor.Chain chain = new RealInterceptorChain(interceptors,null,null,null,0,originalRequest,this,eventListener); ? return chain.proceed(originalRequest);

}?

上面的RealInterceptorChain方法具體實(shí)現(xiàn)严里。在RealCall里的getResponseWithInterceptorChain方法里,創(chuàng)建了一個RealInterceptorChain對象追城,調(diào)用proceed(),在interceptor的intercept()方法里又調(diào)用proceed()刹碾,明顯形成了一個遞歸,像鏈表一下一個一個遞歸傳遞并且做相應(yīng)的攔截處理座柱。

RealInterceptorChain next=newRealInterceptorChain(interceptors,streamAllocation,httpCodec,

connection,index+1,request,call,eventListener);

Interceptor interceptor=interceptors.get(index);

Response response=interceptor.intercept(next);

最后一個攔截器返回處理的Response,也就是服務(wù)器端返回的結(jié)果迷帜,并且在代碼中加了許多的判斷如:this.httpCodec!=null 意思就是還沒有攔截鏈執(zhí)行完物舒。interceptor必須執(zhí)行一次proceed()方法,否則會拋異常戏锹。

RealInterceptorChain + Interceptor實(shí)現(xiàn)了裝飾器模式冠胯,實(shí)現(xiàn)了請求/響應(yīng)的串式或流式處理。只不過內(nèi)層裝飾器不是外層裝飾器的成員變量锦针,而是接口方法中創(chuàng)建的臨時變量荠察。

在ConnectInterceptor之后的攔截器必須滿足:request的url要一致,interceptor必須執(zhí)行一次proceed()奈搜。這樣子做是為了保證遞推的正常運(yùn)作悉盆。而對與client.interceptors是在ConnectInterceptor之前的攔截器,可以不用必須執(zhí)行一次proceed()媚污∫ㄆ埃可以實(shí)現(xiàn)直接返回虛擬的response用于是測試等功能。

這幾個Interceptor的職責(zé):

RetryAndFollowUpInterceptor --->創(chuàng)建StreamAllocation對象耗美,處理http的redirect京髓,出錯重試。對后續(xù)Interceptor的執(zhí)行的影響:修改request及StreamAllocation商架。

BridgeInterceptor-------------->補(bǔ)全缺失的一些http header堰怨。對后續(xù)Interceptor的執(zhí)行的影響:修改request。

CacheInterceptor-------------->處理http緩存蛇摸。對后續(xù)Interceptor的執(zhí)行的影響:若緩存中有所需請求的響應(yīng)备图,則后續(xù)Interceptor不再執(zhí)行。

ConnectInterceptor------------>借助于前面分配的StreamAllocation對象建立與服務(wù)器之間的連接赶袄,并選定交互所用的協(xié)議是HTTP 1.1還是HTTP 2揽涮。對后續(xù)Interceptor的執(zhí)行的影響:創(chuàng)建了httpStream和connection。

CallServerInterceptor----------->處理IO饿肺,與服務(wù)器進(jìn)行數(shù)據(jù)交換蒋困。對后續(xù)Interceptor的執(zhí)行的影響:為Interceptor鏈中的最后一個Interceptor,沒有后續(xù)Interceptor

2.3client.dispatcher().finished(this);?

dispatcher中finish方法的具體實(shí)現(xiàn)(注意第三個參數(shù)的差別)

/** Used by {@codeCall #execute} to signal completion.同步方法 */

void finished(RealCall call) {

? ? finished(runningSyncCalls,call,false);

}

/** Used by {@codeAsyncCall#run} to signal completion. 異步方法*/

void ?finished(AsyncCall call) {

? ? finished(runningAsyncCalls,call,true);

}

上面的finished(runningAsyncCalls,call,true);方法實(shí)現(xiàn)如下:

private?void finished(Dequecalls,T call,boolean promoteCalls) {

? ?int ?runningCallsCount;

? ?Runnable idleCallback;

? synchronized(this) {

? ? if (!calls.remove(call) ) throw newAssertionError("Call wasn't in-flight!");

? ? ? if(promoteCalls) ?promoteCalls(); // 異步Call數(shù)量多時需要維護(hù)隊列敬辣,而不是立即執(zhí).行雪标,所以要加入到runningAsyncCalls中

? ? ? ?runningCallsCount = runningCallsCount();

? ? ? ?idleCallback = this.idleCallback;

? }

? if(runningCallsCount==0&&idleCallback!=null) {

? ? idleCallback.run();

? }

}

上面promoteCalls方法的具體實(shí)現(xiàn)如下:

private int maxRequests=64;

private int maxRequestsPerHost=5;

private void promoteCalls() {

? ? if(runningAsyncCalls.size()>=maxRequests) return;// 已經(jīng)達(dá)到最大容器.

? ? ?if ( readyAsyncCalls.isEmpty() ) return; // 隊列為空

? ? ? ? for(Iterator?i=readyAsyncCalls.iterator();i.hasNext();) { ?// 遍歷取出

? ? ? ? ? AsyncCall call=i.next(); ?

?// 當(dāng)正在執(zhí)行的任務(wù)總數(shù)及相同host下的任務(wù)數(shù)小于最大值時,直接執(zhí)行當(dāng)前請求溉跃,而任務(wù)數(shù)超過限定時村刨,將其加入等待隊列。

? ? ? ? ? if ( runningCallsForHost(call) < maxRequestsPerHost ){?

? ? ? ? ? ? ?i.remove();

? ? ? ? ? ? runningAsyncCalls.add(call);

? ? ? ? ? ? executorService().execute(call);

? ? ? ? ? }

? ? ?}

? ? if ( runningAsyncCalls.size()>=maxRequests ) return;// Reached max capacity.

?}

}

還可以通setMaxRequests()設(shè)置同時允許執(zhí)行的最大請求數(shù)撰茎,以及setMaxRequestsPerHost()設(shè)置相同host下最多運(yùn)行的請求數(shù)嵌牺。從源碼中看出OkHttpClient用了許多的Budiler設(shè)計模式,幾個重要的類Response、Request髓梅、OkHttpClient拟蜻。

2、異步請求過程解析

RealCall的enqueue方法枯饿,其中遇到的大部分方法在同步請求中有分析就不贅述了酝锅。主要講一下異步網(wǎng)絡(luò)請求的流程

@Override

public voidenqueue(Callback responseCallback) {

? synchronized(this) {

? ? ? if(executed)throw newIllegalStateException("Already Executed"); ?

? ? ?executed=true;

?}

? ? captureCallStackTrace();

? ? client.dispatcher().enqueue(newAsyncCall(responseCallback));

}

上面client.dispatcher().enqueue(newAsyncCall(responseCallback));中Dispatcher的enqueue方法

synchronized void enqueue(AsyncCall call) {

// 如果正在執(zhí)行異步請求的隊列沒有超過最大請求數(shù)量,并且有沒有超過每個主機(jī)允許的最大訪問量

// 就將請求加入到正在執(zhí)行異步請求的隊列中奢方,否則就加入到準(zhǔn)備異步請求的隊列中去

? if(runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost

? ? ?runningAsyncCalls.add(call);?

? ? ?executorService().execute(call);

? ?}else{

? ? ? readyAsyncCalls.add(call);

? ?}


異步請求返回是通過接口回調(diào)的具體實(shí)現(xiàn)如下:

final classAsyncCallextendsNamedRunnable{

private finalCallback responseCallback;

AsyncCall(Callback responseCallback) {

? ? super("OkHttp %s",redactedUrl());

? ? this.responseCallback=responseCallback;

}

String host() {

? ? return ?originalRequest.url().host();

}

Request request() {

? return originalRequest;

}

RealCall get() {

? ? return ?RealCall.this;

}

@Override

protected voidexecute() {

? ?booleansignalledCallback=false;

try{

? ? Response response=getResponseWithInterceptorChain();

? ? if (retryAndFollowUpInterceptor.isCanceled() ) {

? ? ? ?signalledCallback=true;

? ? ? ?responseCallback.onFailure(RealCall.this,newIOException("Canceled"));

? ? }else{

? ? ? signalledCallback=true;

? ? ? responseCallback.onResponse(RealCall.this,response);

}

? }catch(IOException e) {

? ? ? ? if(signalledCallback) {

? ? ? ? ? // Do not signal the callback twice!

? ? ? ? ? Platform.get().log(INFO,"Callback failure for "+toLoggableString(),e);

? ? ? }else{

? ? ? ? ? responseCallback.onFailure(RealCall.this,e);

? ? ? ?}

? ?}finally{

? ? ?client.dispatcher().finished(this);

? ?}

?}

}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末搔扁,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子蟋字,更是在濱河造成了極大的恐慌稿蹲,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鹊奖,死亡現(xiàn)場離奇詭異苛聘,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)忠聚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進(jìn)店門设哗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人两蟀,你說我怎么就攤上這事网梢。” “怎么了赂毯?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵战虏,是天一觀的道長。 經(jīng)常有香客問我党涕,道長烦感,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任膛堤,我火速辦了婚禮啸盏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘骑祟。我一直安慰自己,他們只是感情好气笙,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布次企。 她就那樣靜靜地躺著,像睡著了一般潜圃。 火紅的嫁衣襯著肌膚如雪缸棵。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天谭期,我揣著相機(jī)與錄音堵第,去河邊找鬼吧凉。 笑死,一個胖子當(dāng)著我的面吹牛踏志,可吹牛的內(nèi)容都是我干的阀捅。 我是一名探鬼主播,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼针余,長吁一口氣:“原來是場噩夢啊……” “哼饲鄙!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起圆雁,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤忍级,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后伪朽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體轴咱,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年烈涮,在試婚紗的時候發(fā)現(xiàn)自己被綠了朴肺。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡跃脊,死狀恐怖宇挫,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情酪术,我是刑警寧澤器瘪,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站绘雁,受9級特大地震影響橡疼,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜庐舟,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一欣除、第九天 我趴在偏房一處隱蔽的房頂上張望挪略。 院中可真熱鬧历帚,春花似錦、人聲如沸杠娱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽摊求。三九已至禽拔,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背睹栖。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工硫惕, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人野来。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓恼除,卻偏偏與公主長得像,于是被迫代替她去往敵國和親梁只。 傳聞我的和親對象是個殘疾皇子缚柳,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 這篇文章主要講 Android 網(wǎng)絡(luò)請求時所使用到的各個請求庫的關(guān)系,以及 OkHttp3 的介紹搪锣。(如理解有誤秋忙,...
    小莊bb閱讀 1,157評論 0 4
  • 這段時間老李的新公司要更換網(wǎng)絡(luò)層,知道現(xiàn)在主流網(wǎng)絡(luò)層的模式是RxJava+Retrofit+OKHttp,所以老李...
    隔壁老李頭閱讀 32,776評論 51 406
  • OkHttp作為時下最受歡迎的網(wǎng)絡(luò)請求框架之一构舟,它有著自己的優(yōu)點(diǎn): 使用了眾多的設(shè)計模式(如:Builder模式灰追、...
    永恒之眼V閱讀 301評論 1 1
  • OkHttp源碼分析-同步篇 很早就想拿okhttp開刀了,這次就記一次使用OKhttp的網(wǎng)絡(luò)請求狗超。首先需要說明的...
    埃賽爾閱讀 978評論 1 2
  • 不知從什么時候開始弹澎,我也開始流入“曬朋友圈”的大潮,直到上個星期努咐,拉開相冊的小框苦蒿,然后,便是腦海的一瞬空白渗稍。 ——...
    慧君慧君閱讀 857評論 0 1