OKHttp 源碼分析

基本使用

  1. 異步GET

     Request.Builder builder = new Request.Builder().url("https://www.baidu.com/");
     builder.method("GET",null);
     Request build = builder.build();
     OkHttpClient client = new OkHttpClient();
     Call call = client.newCall(build);
     call.enqueue(new Callback() {
         @Override
         public void onFailure(Call call, IOException e) {
    
         }
    
         @Override
         public void onResponse(Call call, Response response) throws IOException {
             System.out.println(response.body().string());
         }
     }); 
    
  2. 異步POST

     FormBody.Builder builder = new FormBody.Builder().add("XXX", "XXX");
     FormBody build = builder.build();
     Request request = new Request.Builder().url("https://.........").post(build).build();
     OkHttpClient client = new OkHttpClient();
     Call call = client.newCall(request);
     call.enqueue(new Callback() {
         @Override
         public void onFailure(Call call, IOException e) {
    
         }
    
         @Override
         public void onResponse(Call call, Response response) throws IOException {
             System.out.println(response.body().string());
         }
     });
    

用法都非常簡單巍佑,只用簡單的調(diào)用一下,就能實(shí)現(xiàn)所想需要實(shí)現(xiàn)的GET苛预,POST請求句狼。

源碼分析

我們還是從請求處理開始分析起:

Call call = client.newCall(request);

我們通常就會(huì)這樣聲明一個(gè) Call 對象 然后執(zhí)行異步 enqueue() 或者同步 execute()方法。

點(diǎn)進(jìn) newCall 里面:

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

這里實(shí)際就是返回了 RealCall 這個(gè)對象热某, 而接下來 網(wǎng)絡(luò)請求 實(shí)際上就是調(diào)用 RealCall 的里面的方法腻菇。

分析 -- 異步enqueue()

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

從上面可以得知請求是從 dispatcher 完成的。

接下來分析 dispatcher

在 Dispatcher.java 下

先看看這個(gè)類所定義的變量吧

最大并發(fā)請求數(shù)
private int maxRequests = 64;
每個(gè)主機(jī)最大的請求數(shù)
private int maxRequestsPerHost = 5;

/** Executes calls. Created lazily. */
消費(fèi)者線程池
private @Nullable ExecutorService executorService;

/** Ready async calls in the order they'll be run. */
將要運(yùn)行的異步請求隊(duì)列
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();

/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
正在運(yùn)行的異步請求隊(duì)列
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();

/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
正在運(yùn)行的同步請求隊(duì)列
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

由此可以看出他維護(hù)個(gè)各種隊(duì)列昔馋,把不同的Call放置相對應(yīng)的隊(duì)列中去筹吐,隊(duì)列中的請求最后會(huì)一個(gè)個(gè)的進(jìn)行訪問。

接下來看看構(gòu)造函數(shù)

/** Executes calls. Created lazily. */
private @Nullable ExecutorService executorService;

public Dispatcher(ExecutorService executorService) {
this.executorService = executorService;
}

public Dispatcher() {
}

構(gòu)造函數(shù)有兩個(gè)秘遏,一個(gè)構(gòu)造函數(shù)是帶參的丘薛,這個(gè)參數(shù)就是自己實(shí)現(xiàn)的線程池,如果我們調(diào)用不帶參的構(gòu)造函數(shù)時(shí)邦危,executorService沒有被初始化洋侨。而為他初始化的方法是 executorService () .

public synchronized ExecutorService executorService() {
    if (executorService == null) {
      executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
          new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
    }
    return executorService;
}

從上面看到他會(huì)創(chuàng)建一個(gè)默認(rèn)的線程池 舍扰。

那么接下來在異步請求的時(shí)候會(huì)調(diào)用enqueue() 方法 ,而從下面我們可以看到 當(dāng)異步請求隊(duì)列 數(shù)量小于 64 并且正在運(yùn)行的請求主機(jī)數(shù)小于5時(shí)希坚,會(huì)把請求放置在 runningAsyncCalls 中 并在線程池中執(zhí)行边苹。 否則就加入到 readyAsyncCalls 中進(jìn)行緩存。

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

然后就會(huì)執(zhí)行以下方法

executorService().execute(call);

executorService()會(huì)返回一個(gè)線程池 裁僧, 如果使用的是默認(rèn)線程池的話:那么在 ThreadPoolExecutor 內(nèi) 个束,所要傳進(jìn)去的是 Runnable 這個(gè)接口,

public void execute(Runnable command) {
    .....   
}

再看看 AsyncCall 這個(gè)類吧 :

AsyncCall 是 RealCall 的 內(nèi)部類 聊疲。 他繼承了NamedRunnable茬底,而NamedRunnable正是實(shí)現(xiàn)了Runnable方法。

在NamedRunnable里面實(shí)現(xiàn)了run() 方法 获洲,run() 里面調(diào)用了他的一個(gè) 抽象方法 :

protected abstract void execute();

那么子類就只要實(shí)現(xiàn) execute 方法就可以了:

所以就回到了 AsyncCall 中實(shí)現(xiàn)的 execute() 方法

@Override protected void execute() {
  try{
  ......
  } finally {
    client.dispatcher().finished(this);
  }
}

先來看看這個(gè)方法最后都做了什么吧 :

無論如何都會(huì)調(diào)用到 client.dispatcher().finished(this); 這個(gè)方法

進(jìn)去 finished 方法 阱表,我們看到最后調(diào)用的方法為以下:

void finished(AsyncCall call) {
    finished(runningAsyncCalls, call, true);
}

private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
    int runningCallsCount;
    Runnable idleCallback;
    synchronized (this) {
      
      注 1   
      if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
      if (promoteCalls) promoteCalls();

      runningCallsCount = runningCallsCount();
      idleCallback = this.idleCallback;
    }
    
    if (runningCallsCount == 0 && idleCallback != null) {
      idleCallback.run();
    }
}

從上面可以看到 calls 是 runningAsyncCalls ,首先他先從隊(duì)列移除昌妹,然后執(zhí)行
promoteCalls() 捶枢,隨后將更新 runningCallsCount 和 idleCallback 。

主要的方法就是 promoteCalls()

private void promoteCalls() {
    if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
    if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.
    
    for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
      AsyncCall call = i.next();
    
      if (runningCallsForHost(call) < maxRequestsPerHost) {
        i.remove();
        runningAsyncCalls.add(call);
        executorService().execute(call);
      }
    
      if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
    }
}

從for語句可以明顯看出 因?yàn)?runningAsyncCalls 的請求被移除了 飞崖, 那么就會(huì)從 readyAsyncCalls 中拿出請求向 runningAsyncCalls 補(bǔ)充 烂叔。

接著我們回到 execute() 方法 ,看看其中實(shí)現(xiàn)了什么:

@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 {
      eventListener.callFailed(RealCall.this, e);
      responseCallback.onFailure(RealCall.this, e);
    }
  } finally {
    client.dispatcher().finished(this);
  }
}

從名字可以看出 Response 就是請求的結(jié)果固歪, 而網(wǎng)絡(luò)請求交給了
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, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());
    
    return chain.proceed(originalRequest);
}

構(gòu)建了一大堆攔截器蒜鸡,最后 創(chuàng)建了 RealInterceptorChain 這個(gè) 對象,他實(shí)際作用就是一個(gè) 攔截鏈 牢裳。

最后把請求交給了攔截鏈的proceed () 方法:

@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 {
    
    .....
    
    注 1 
    RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
        connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
        writeTimeout);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);
    
    .....
    
    return response;
}

注 1 : 從上面可以看出了這個(gè)攔截鏈會(huì)不斷的調(diào)用下一個(gè)攔截器逢防。從而現(xiàn)在分析一下所添加的攔截器吧。

在getResponseWithInterceptorChain() 方法中蒲讯,里面就是添加了一堆攔截器

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));

在拿取攔截鏈里面的攔截器忘朝,然后一一執(zhí)行。

retryAndFollowUpInterceptor -- 這攔截器主要是做重試判帮,網(wǎng)絡(luò)錯(cuò)誤局嘁,以及請求重定向的一些操作。

BridgeInterceptor -- 這個(gè)攔截器晦墙,主要把用戶的請求轉(zhuǎn)換為網(wǎng)絡(luò)的請求悦昵,負(fù)責(zé)對Request和Response報(bào)文進(jìn)行加工。

CacheInterceptor -- 緩存攔截器

ConnectInterceptor -- 連接攔截器晌畅,主要是處理連接服務(wù)器但指,以及http , https的包裝

CallServerInterceptor -- 服務(wù)攔截器,主要是發(fā)送(write棋凳、input)、讀忍巴瘛(read卢肃、output)數(shù)據(jù)才顿。也是攔截器的最后一個(gè)環(huán)節(jié)莫湘,這里就真正拿到了網(wǎng)絡(luò)的結(jié)果了。

攔截器會(huì)不斷的調(diào)用下一個(gè)攔截器郑气,最后全部執(zhí)行完幅垮。在每個(gè)攔截器里面,你可以看到類似的代碼 : Response networkResponse = chain.proceed(...); 這就是調(diào)用了下一個(gè)攔截器尾组。

攔截器所要執(zhí)行的內(nèi)容基本就如上的簡述了忙芒。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市讳侨,隨后出現(xiàn)的幾起案子呵萨,更是在濱河造成了極大的恐慌,老刑警劉巖跨跨,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件潮峦,死亡現(xiàn)場離奇詭異,居然都是意外死亡勇婴,警方通過查閱死者的電腦和手機(jī)忱嘹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來耕渴,“玉大人拘悦,你說我怎么就攤上這事〕髁常” “怎么了础米?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長椭盏。 經(jīng)常有香客問我,道長乌叶,這世上最難降的妖魔是什么事扭? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮罐农,結(jié)果婚禮上涵亏,老公的妹妹穿的比我還像新娘气筋。我一直安慰自己,他們只是感情好光稼,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著冰垄,像睡著了一般虹茶。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上要门,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天封豪,我揣著相機(jī)與錄音吹埠,去河邊找鬼缘琅。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的滚局。 我是一名探鬼主播藤肢,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼最住!你這毒婦竟也來了涨缚?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎珊燎,沒想到半個(gè)月后俐末,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體载矿,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了溺拱。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片迫摔。...
    茶點(diǎn)故事閱讀 40,090評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖纱烘,靈堂內(nèi)的尸體忽然破棺而出擂啥,到底是詐尸還是另有隱情啤它,我是刑警寧澤,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布塌碌,位于F島的核電站翎猛,受9級特大地震影響切厘,放射性物質(zhì)發(fā)生泄漏疫稿。R本人自食惡果不足惜遗座,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望号坡。 院中可真熱鬧,春花似錦冤馏、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽驾茴。三九已至锈至,卻和暖如春峡捡,著一層夾襖步出監(jiān)牢的瞬間们拙,已是汗流浹背砚婆。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留猖吴,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像幌衣,于是被迫代替她去往敵國和親豁护。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評論 2 355

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