okhttp分析

okhttp使用分為同步請求和異步請求:
異步請求:

String url = "";

Request request = new Request.Builder().get().url(url).build();

OkHttpClient okHttpClient = new OkHttpClient();

public void request(){

    okHttpClient.newCall(request).enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {

        }

        @Override
        public void onResponse(Call call, Response response) throws IOException {

        }
    });
}

request是一個請求對像堪置,包含了請求url躬存,methord,heard等信息
okhttpclient 主要用于發(fā)送http請求以及讀取回應
newcall方法主要是用于返回一個realcall對象舀锨,并且把請求對象request交給了realcall

@Override public Call newCall(Request request) {
 return new RealCall(this, request, false /* for web socket */);
 }

接下來看一下realcall的enqueue方法:

@Override public void enqueue(Callback responseCallback) {
 synchronized (this) {
  if (executed) throw new IllegalStateException("Already Executed");
  executed = true;
}
captureCallStackTrace();
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}

首先通過同步岭洲,確保call不會被重復執(zhí)行(如果想要完全相同的call可以通過clone方法),然后利用dispatcher調(diào)度器來執(zhí)行enqueue方法坎匿,下面看一下dispatcher.enqueue方法:

synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
  runningAsyncCalls.add(call);
  executorService().execute(call);
} else {
  readyAsyncCalls.add(call);
}
}

dispatcher就是一個異步請求是的策略盾剩,它里面包含了能同時進行的最大請求數(shù)(默認是64),同時請求相同host的最大數(shù)(默認是5)替蔬,以及維護一個異步任務執(zhí)行隊列和一個異步任務等待隊列告私,然后回來看dispatcher.enqueue方法,首先會判斷當前執(zhí)行隊列是不是超出最大請求數(shù)进栽,以及同時訪問相同host的數(shù)量是不是超過德挣,如果超過就將call放入等待隊列恭垦,否則放入執(zhí)行隊列快毛,并且交給線程池去執(zhí)行
執(zhí)行的為AsyncCall,我們看一下AsyncCall的實現(xiàn):

final class AsyncCall extends NamedRunnable { 

AsyncCall繼承自NamedRunnable番挺,NamedRunnable其實是繼承自Runnable唠帝,在他的run方法中會調(diào)用execute();方法,說到底其實就是會執(zhí)行AsyncCall的execute();方法玄柏,接下來看一下AsyncCall的execute();方法:

@Override protected void execute() {
  boolean signalledCallback = false;
  try {
    Response response = getResponseWithInterceptorChain();
    if (retryAndFollowUpInterceptor.isCanceled()) {
      signalledCallback = true;
      responseCallback.onFailure(RealCall.this, new IOException("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);
  }
}

可以看到真正執(zhí)行請求的是getResponseWithInterceptorChain(),獲取到response后回調(diào)給用戶襟衰,值得注意的是最后finally,會通過調(diào)度器移除隊列粪摘,并且判斷如果執(zhí)行隊列沒有達到最大值則把等待隊列變?yōu)閳?zhí)行隊列瀑晒,這樣就包證了等待隊列的執(zhí)行
下面我們看一下真正請求的getResponseWithInterceptorChain()

Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
interceptors.add(retryAndFollowUpInterceptor);
interceptors.add(new BridgeInterceptor(client.cookieJar()));
interceptors.add(new CacheInterceptor(client.internalCache()));
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
  interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));

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

我們可以看到方法中首先創(chuàng)建一個攔截器的列表,添加各個攔截器徘意,每個攔截器個自完成自己的任務苔悦,并且將不屬于自己的任務交給下一個,這就是一種責任鏈模式椎咧,最后的執(zhí)行是由chain.proceed(originalRequest);來實現(xiàn)的玖详,責任鏈中每個攔截器都會執(zhí)行chain.proceed()方法之前的代碼,等責任鏈最后一個攔截器執(zhí)行完畢后會返回最終的響應數(shù)據(jù),而chain.proceed() 方法會得到最終的響應數(shù)據(jù)蟋座,這時就會執(zhí)行每個攔截器的chain.proceed()方法之后的代碼拗踢,其實就是對響應數(shù)據(jù)的一些操作。

@Override public Response proceed(Request request) throws IOException {
return proceed(request, streamAllocation, httpCodec, connection);
}
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
  RealConnection connection) throws IOException {
if (index >= interceptors.size()) throw new AssertionError();

calls++;

// If we already have a stream, confirm that the incoming request will use it.
if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) {
  throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
      + " must retain the same host and port");
}

// If we already have a stream, confirm that this is the only call to chain.proceed().
if (this.httpCodec != null && calls > 1) {
  throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
      + " must call proceed() exactly once");
}

// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(
    interceptors, streamAllocation, httpCodec, connection, index + 1, request);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);

// Confirm that the next interceptor made its required call to chain.proceed().
if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
  throw new IllegalStateException("network interceptor " + interceptor
      + " must call proceed() exactly once");
}

// Confirm that the intercepted response isn't null.
if (response == null) {
  throw new NullPointerException("interceptor " + interceptor + " returned null");
}

return response;
}

簡述okhttp的執(zhí)行流程:

  • OkhttpClient 實現(xiàn)了Call.Fctory,負責為Request 創(chuàng)建 Call向臀;
  • RealCall 為Call的具體實現(xiàn)巢墅,其enqueue() 異步請求接口通過Dispatcher()調(diào)度器利用ExcutorService實現(xiàn),而最終進行網(wǎng)絡請求時和同步的execute()接口一致飒硅,都是通過 getResponseWithInterceptorChain() 函數(shù)實現(xiàn)
  • getResponseWithInterceptorChain() 中利用 Interceptor 鏈條砂缩,責任鏈模式 分層實現(xiàn)緩存、透明壓縮三娩、網(wǎng)絡 IO 等功能庵芭;最終將響應數(shù)據(jù)返回給用戶。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末雀监,一起剝皮案震驚了整個濱河市双吆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌会前,老刑警劉巖好乐,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異瓦宜,居然都是意外死亡蔚万,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進店門临庇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來反璃,“玉大人,你說我怎么就攤上這事假夺』打冢” “怎么了?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵已卷,是天一觀的道長梧田。 經(jīng)常有香客問我,道長侧蘸,這世上最難降的妖魔是什么裁眯? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮讳癌,結(jié)果婚禮上穿稳,老公的妹妹穿的比我還像新娘。我一直安慰自己析桥,他們只是感情好司草,可當我...
    茶點故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布艰垂。 她就那樣靜靜地躺著,像睡著了一般埋虹。 火紅的嫁衣襯著肌膚如雪猜憎。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天搔课,我揣著相機與錄音胰柑,去河邊找鬼。 笑死爬泥,一個胖子當著我的面吹牛柬讨,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播袍啡,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼踩官,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了境输?” 一聲冷哼從身側(cè)響起蔗牡,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎嗅剖,沒想到半個月后辩越,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡信粮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年黔攒,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片强缘。...
    茶點故事閱讀 40,137評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡督惰,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出欺旧,到底是詐尸還是另有隱情姑丑,我是刑警寧澤蛤签,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布辞友,位于F島的核電站,受9級特大地震影響震肮,放射性物質(zhì)發(fā)生泄漏称龙。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一戳晌、第九天 我趴在偏房一處隱蔽的房頂上張望鲫尊。 院中可真熱鬧,春花似錦沦偎、人聲如沸疫向。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽搔驼。三九已至谈火,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間舌涨,已是汗流浹背糯耍。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留囊嘉,地道東北人温技。 一個月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像扭粱,于是被迫代替她去往敵國和親舵鳞。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,086評論 2 355

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