okhttp源碼簡單流程分析

攔截器詳細解析可以看大佬簡書 "https://www.jianshu.com/p/6fac73f7570f"和 “https://www.jianshu.com/p/3c740829475c”

在這里插入圖片描述

okhttp請求流程
1:OkHttpClient okHttpClient = new OkHttpClient.Builder()
構建一個okhttpClient對象,傳入你想傳入的對象,不傳就是默認的;
2:構建request對象
Request request = new Request.Builder()
3:okHttpClient.newCall 實際上返回的realCall類 繼續調用RealCall.newRealCall
4:調用enqueue方法,傳入我們需要的回調接口,而且會判斷,
synchronized (this) {
if (executed) throw new IllegalStateException(“Already Executed”);
executed = true;
}
如果當前這個call對象已經被運行的話,則拋出異常;
5:繼續調用dispatcher的enqueue方法,如果當前運行隊列<64并且正在運行,訪問同一個服務器地址的請求<5
就直接添加到運行隊列,并且開始運行;
不然就添加到等待隊列;
6:運行AsyncCall,調用它的execute方法
7:在execute方法中處理完response之后,會在finally中調用dispathcer的finished方法;
8:當當前已經處理完畢的call從運行隊列中移除掉;并且調用promoteCalls方法
9:promoteCalls方法中進行判斷,
如果運行隊列數目大于等于64,如果等待隊列里啥都沒有,也直接return?
循環等待隊列,
將等待隊列中的數據進行移除,移除是根據運行隊列中還能存放多少來決定;
移到了運行隊列中,并且開始運行;

OkHttpClient okHttpClient = new OkHttpClient.Builder().build();Request request = new Request.Builder().build();//newCall方法是調用RealCall的newRealCall返回一個RealCallCall call = okHttpClient.newCall(request);//執行請求call.enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {}@Overridepublic void onResponse(Call call, Response response) throws IOException {}});//這里第二次執行會直接報IllegalStateException錯誤并提示Already Executedcall.enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {}@Overridepublic void onResponse(Call call, Response response) throws IOException {}});

進入call.enqueue方法

@Override public void enqueue(Callback responseCallback) {//這個鎖是為了防止重復請求 如果你同一個call enqueue多次就會直接返回Already Executedsynchronized (this) {if (executed) throw new IllegalStateException("Already Executed");executed = true;}captureCallStackTrace();eventListener.callStart(this);//這里的enqueue是傳入任務線程AsyncCall并分配到運行隊列或等待隊列client.dispatcher().enqueue(new AsyncCall(responseCallback));}

進入enqueue方法

//okhttp里執行任務分為兩個隊列 運行隊列和等待隊列
synchronized void enqueue(AsyncCall call) {//當運行的隊列中的數值小于64, //并且同時訪問同一個機器目標HOST請求書小于5直接加入到運行隊列不然的話就加入到等待隊列if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {runningAsyncCalls.add(call);//這里維護著請求用的線程池executorService().execute(call);} else {readyAsyncCalls.add(call);}}
  public synchronized ExecutorService executorService() {if (executorService == null) {//1:核心線程數 保持在線程池中的線程數量//2:線程池最大可容納的線程數  //3參數:當線程池中的線程數量大于核心線程數,空閑線程就會等待60s才會被終止,如果小于就會立刻停止;executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));}return executorService;}
//這個就是隊列中傳入的線程NameRunnable繼承自Runnable
final class AsyncCall extends NamedRunnable {private final Callback 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;}//這個就是Async的run方法@Override protected void execute() {boolean signalledCallback = false;try {//getResponseWithInterceptorChain() 添加攔截器 okhttp的責任鏈設計也在這里//response就是請求結果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);}}}

添加攔截器

//添加攔截器 責任鏈設計 
//攔截器詳細解析可以看大佬簡書 "https://www.jianshu.com/p/6fac73f7570f"和 "https://www.jianshu.com/p/3c740829475c"
Response getResponseWithInterceptorChain() throws IOException {// Build a full stack of interceptors.List<Interceptor> interceptors = new ArrayList<>();interceptors.addAll(client.interceptors());//重試/重定向攔截器 連接失敗后進行重試、對請求結果跟進后進行重定向interceptors.add(retryAndFollowUpInterceptor);//橋攔截器:連接應用程序和服務器的橋梁,我們發出的請求會經過它的處理才能發給服務器,//比如設置請求內容的長度 封裝header屬性 host keep-live gzip header 進行基本設置,interceptors.add(new BridgeInterceptor(client.cookieJar()));//緩存攔截器 在發出請求前,先判斷是否命中緩存,//如果命中則可以不請求,直接使用緩存的響應(默認只會對Get請求進行緩存);//如果未命中則進行網絡請求,并將結果緩存,等待下次請求被命中。interceptors.add(new CacheInterceptor(client.internalCache()));//連接攔截器 與服務器建立連接。interceptors.add(new ConnectInterceptor(client));if (!forWebSocket) {interceptors.addAll(client.networkInterceptors());}//與服務器通信;封裝請求數據與解析響應數據(如:HTTP報文)。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);}

在execute方法中處理完response之后,會在finally中調用dispathcer的finished方法;

//將當前已經處理完畢的call從運行隊列中移除掉;并且調用promoteCalls方法
private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {int runningCallsCount;Runnable idleCallback;synchronized (this) {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();}}

promoteCalls方法中進行判斷

//如果運行隊列數目大于等于64,如果等待隊列里啥都沒有,也直接return
//循環等待隊列,
//將等待隊列中的數據進行移除,移除是根據運行隊列中還能存放多少來決定;
//移到了運行隊列中,并且開始運行;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.}}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/41127.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/41127.shtml
英文地址,請注明出處:http://en.pswp.cn/news/41127.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

vector使用以及模擬實現

vector使用以及模擬實現 vector介紹vector常用接口1.構造2.迭代器3.容量4.增刪查改5.練習 vector模擬實現1.迭代器失效2.反向迭代器3.完整代碼 vector介紹 和我們原來講的string不同&#xff0c;vector并不是類&#xff0c;是一個類模板&#xff0c;加<類型>實例化以后才…

主機防護的重要性和方式

01 主機防護的重要性 主機防護是網絡安全的重要組成部分。在互聯網時代&#xff0c;網絡攻擊成為了一種常見的威脅&#xff0c;而主機防護則是保護計算機系統免受網絡攻擊的重要手段。 主機防護可以防范各種網絡攻擊&#xff0c;如病毒、木馬、黑客攻擊等&#xff0c;從而保…

氣象監測站:用科技感知氣象變化

氣象監測站是利用科學技術感知當地小氣候變化情況的氣象觀測儀器&#xff0c;可用于農業、林業、養殖業、畜牧業、環境保護、工業等多個領域&#xff0c;提高對環境數據的利用率&#xff0c;促進產業效能不斷提升。 氣象監測站主要由氣象傳感器、數據傳輸系統、電源系統、支架…

Linux debian12解壓和壓縮.rar文件教程

一、Debian12安裝rar命令 sudo apt install rar二、使用rar軟件 1.解壓文件 命令格式&#xff1a; rar x 文件名.rar實力測試&#xff1a; [rootdoudou tmp]# rar x test.rar2.壓縮文件 test是一個文件夾 命令格式&#xff1a; rar a 文件名.rar 文件夾名實例測試&#x…

centos7 yum獲取軟件所有依賴包 創建本地yum源 yum離線安裝軟件

centos7 yum獲取軟件所有依賴包 創建本地yum源 離線安裝軟件 1、以安裝docker 20.10為例2、centos7 yum獲取docker 20.10 所有依賴包3、創建本地docker yum源4、yum使用本地docker源 離線安裝docker 1、以安裝docker 20.10為例 參考鏈接&#xff1a; 添加docker 清華軟件源 y…

git環境超詳細配置說明

一&#xff0c;簡介 在git工具安裝完成之后&#xff0c;需要設置一下常用的配置&#xff0c;如郵箱&#xff0c;縮寫&#xff0c;以及git commit模板等等。本文就來詳細介紹些各個配置如何操作&#xff0c;供參考。 二&#xff0c;配置步驟 2.1 查看當前git的配置 git conf…

使用 Apache Kafka 和 Go 將數據引入 OpenSearch

需要編寫自定義集成層來滿足數據管道中的特定要求&#xff1f;了解如何使用 Go 通過 Kafka 和 OpenSearch 實現此目的。 可擴展的數據攝取是OpenSearch等大規模分布式搜索和分析引擎的一個關鍵方面。構建實時數據攝取管道的方法之一是使用Apache Kafka。它是一個開源事件流平臺…

單詞倒排(C語言詳解)

題目&#xff1a;單詞倒排 描述&#xff1a;對字符串中的所有單詞進行倒排。 說明&#xff1a; 1、構成單詞的字符只有26個大寫或小寫英文字母&#xff1b; 2、非構成單詞的字符均視為單詞間隔符&#xff1b; 3、要求倒排后的單詞間隔符以一個空格表示&#xff1b;如果原字…

米爾瑞薩RZ/G2L開發板-02 ffmpeg的使用和RTMP直播

最近不知道是不是熬夜太多&#xff0c;然后記憶力減退了&#xff1f; 因為板子回來以后我就迫不及待的試了一下板子&#xff0c;然后發現板子有SSH&#xff0c;但是并沒有ffmpeg&#xff0c;最近總是在玩&#xff0c;然后今天說是把板子還原一下哇&#xff0c;然后把官方的固件…

前端單點登錄SSO面試回答

JWT鑒權機制 1.JWT用于登錄身份驗證 2.用戶登錄成功后&#xff0c;后端通過JWT機制生成一個token&#xff0c;返回給客戶端 3.客戶端后續的每次請求都需要攜帶token&#xff0c;放在header的authorization中 4.后端從authorization中拿到token后&#xff0c;通過secretKey進…

Spring Boot中使用validator如何實現接口入參自動檢驗

文章目錄 一、背景二、使用三、舉例 一、背景 在項目開發過程中&#xff0c;經常會對一些字段進行校驗&#xff0c;比如字段的非空校驗、字段的長度校驗等&#xff0c;如果在每個需要的地方寫一堆if else 會讓你的代碼變的冗余笨重且相對不好維護&#xff0c;如何更加規范和優…

微服務-GateWay(網關)

所謂網關是什么意思&#xff1f; 相當于就是你們小區家的保安&#xff0c;進出小區都得獲得保安的同意&#xff0c;守護你們小區的生命財產健康&#xff0c;網關也是如此&#xff0c;對每個請求都嚴格把關&#xff0c;將合法的或者是獲得權限的請求進入服務器 網關的功能&…

設計模式之解釋器模式詳解及實例

1、解釋器設計模式概述&#xff1a; 解釋器模式&#xff08;Interpreter Pattern&#xff09;是一種設計模式&#xff0c;它主要用于描述如何構建一個解釋器以解釋特定的語言或表達式。該模式定義了一個文法表示和解釋器的類結構&#xff0c;用于解釋符合該文法規則的語句。解…

擴散模型實戰(四):從零構建擴散模型

推薦閱讀列表&#xff1a; 擴散模型實戰&#xff08;一&#xff09;&#xff1a;基本原理介紹 擴散模型實戰&#xff08;二&#xff09;&#xff1a;擴散模型的發展 擴散模型實戰&#xff08;三&#xff09;&#xff1a;擴散模型的應用 本文以MNIST數據集為例&#xff0c;從…

智能樓宇綜合布線實訓室建設方案

一、樓宇智能綜合布線實訓室方案概述 樓宇智能綜合布線實訓室方案旨在為學生提供一個真實的學習和實踐環境&#xff0c;以培養他們在樓宇智能綜合布線領域的實際操作能力和技能。以下是一個概述&#xff1a; 1. 培養目標&#xff1a;培養學生在樓宇智能綜合布線方面的綜合能力…

Shader學習(三)(片元著色器)

1、在片元著色器處理漫反射 // Upgrade NOTE: replaced _World2Object with unity_WorldToObjectShader "Custom/specularfragement" {properties{_sp("Specular",color) (1,1,1,1)_shiness("Shiness",range(1,64)) 8}SubShader{pass {tags{&…

深入理解設計模式-行為型之模板(和回調區別聯系)

概述 模板設計模式&#xff08;Template Design Pattern&#xff09;是一種行為型設計模式&#xff0c;它定義了一個算法的骨架&#xff0c;將算法的一些步驟延遲到子類中實現。模板設計模式允許子類在不改變算法結構的情況下重新定義算法的某些步驟。 模板設計模式的核心思想…

網絡通信原理應用層(第五十一課)

1)DNS:域名解析系統,端口號TCP或UDP的53 2)域名注冊網站 -新網 www.xinnet.com -萬網-阿里云 www.net.cn -中國互聯 hulian.top 配置通過域名訪問網站(NETBASE第七課)_IHOPEDREAM的博客-CSDN博客 2、FTP 1)FTP概述 -文件傳輸協議 -控制連接:TCP 21 <

對redis、redisson、springcache總結

<一> redis-緩存中間件 什么是redis redis是c語言開發的&#xff0c;一個高性能key-value鍵值對內存數據庫&#xff0c;可以用來做數據庫、緩存、消息中間件的一種非關系型數據庫。 redis數據存儲在哪里 內存和磁盤中&#xff0c;但是redis的讀寫都在內存中&#xff0c;…

leetcode-413. 等差數列劃分(java)

等差數列劃分 leetcode-413. 等差數列劃分題目描述雙指針 上期經典算法 leetcode-413. 等差數列劃分 難度 - 中等 原題鏈接 - 等差數列劃分 題目描述 如果一個數列 至少有三個元素 &#xff0c;并且任意兩個相鄰元素之差相同&#xff0c;則稱該數列為等差數列。 例如&#xff0…