A. Android網路請求庫【OkHttp4.9.3】基本用法與原理分析
OkHttp是一套處理 HTTP 網路請求的依賴庫,由 Square 公司設計研發並開源,目前可以在 java 和 Kotlin 中使用。對於 Android App 來說,OkHttp 現在幾乎已經占據了所有的網路請求操作,Retrofit + OkHttp實現網路請求似乎成了一種標配。因此它也是每一個 Android 開發工程師的必備技能,了解其內部實現原理可以更好地進行功能擴展、封裝以及優化。
OkHttp的高效性體現在:
第一步:創建OkHttpClient,創建OkHttpClient有兩種方式:
OkHttpClient提供了豐富的配置方法,例如添加攔截器、指定連接池、設置請求超時等等。
第二步:創建請求
使用Request.Builder() 構建Request實例
第三步:發起網路請求
OkHttp支持同步和非同步兩種請求方式
OkHttp的使用方法非常簡單,三步操作就可以發起一個簡單的同步或非同步請求。我們也可以很輕松地對網路請求進行配置,例如添加請求頭、設置請求方式、設置請求超時等等,這些配置參數會在源碼分析過程中詳細介紹。
現在我們已經學會了三步操作發起網路請求,接下來以這三個步驟為切入點,深入到源碼中學習OkHttp的實現原理,廢話少說馬上開車。
OkHttpClient創建方式有兩種,我們看看兩種方式有什麼區別。
第一種直接使用默認構造函數,內部依然是使用建造者模式
第二種使用建造者模式
兩種方式最終都是調用構造函數OkHttpClient(builder:Builder),由參數builder負責所有的參數配置工作。
當您創建單個OkHttpClient實例並將其用於所有 HTTP 調用時,OkHttp 性能最佳。 這是因為每個OkHttpClient都擁有自己的連接池和線程池,重用連接和線程可減少延遲並節省內存。 相反,為每個請求創建一個客戶端會浪費空閑池上的資源。
Request同樣使用建造者模式來創建,這里貼上部分重要源碼,很簡單就不細說了。
OkHttp發起網路請求分為同步請求和非同步請求兩種方式,我們只分析非同步請求流程,因為只要理解了非同步請求過程,基本上也就明白同步請求是怎麼一回事了。
RealCall是連接應用層與網路層的橋梁,負責處理連接、請求、響應和數據流。
Dispatcher維護著一套非同步任務執行策略,分析策略之前先介紹幾個重要概念:
client.dispatcher.enqueue(AsyncCall(responseCallback)) 執行步驟為:
AsyncCall實現了Runnable介面,因此一旦被線程池中的線程處理就會調用它的run()方法:
話休絮煩,我們開始分析攔截器責任鏈:
責任鏈執行流程:首先獲取當前攔截器interceptor,並且調用interceptor.intercept(next)執行攔截器操作。這里的next表示的是index+1後的責任鏈對象,攔截器的intercept()方法內部會調用next.proceed(request)方法再次進入到責任鏈,由於此時index已經加1,所以處理的是下一個攔截器。
如此循環往復,直到處理完責任鏈上最後一個攔截器為止。
注意除最後一個攔截器CallServerInterceptor不會調用chain.proceed(request)方法之外,其他攔截器都應該至少調用一次chain.proceed(request)方法。
為了驗證上面的結論,我們進入到RetryAndFollowUpInterceptor的intercept()方法一探究竟:
可以看到注釋1處重新進入責任鏈處理下一個攔截器。
有興趣可以自行查看最後一個攔截器CallServerInterceptor源碼,此處只給出本人閱讀源碼後得出的結論:
以上就是攔截器責任鏈的工作流程,我們再通過流程圖仔細感受一下。
分析完攔截器責任鏈,我們繼續分析AsyncCall#run()方法:
我們看到,如果()方法成功獲得服務端返回的數據,則調用responseCallback.onResponse(this@RealCall, response)方法完成非同步回調;如果服務端數據獲取失敗(請求異常),則調用responseCallback.onFailure(this@RealCall, canceledException)方法完成非同步回調
需要注意的是,responseCallback回調是在子線程中完成的,所以如果想把數據顯示到UI上,需要切換回主線程進行UI操作。
OkHttp發起網路請求全過程:
【知識點】OkHttp 原理 8 連問
B. Android性能優化之網路優化DNS和HttpDNS知識詳解
前言小計
本文已在在公眾號【Android開發編程】發表
一、什麼是DNS
二、DNS域名結構
1、DNS域名命名
2、域名的分級
域名可以劃分為各個子域,子域還可以繼續劃分為子域的子域,這樣就形成了頂級域名、二級域名、三級域名等
頂級域名可以分為三大類:
國家頂級域名:cn、us、uk等
通用域名:常見的有7個,com、net、org、e、int、gov、mil
方向域名: arpa,用於將ip地址轉為域名
域名伺服器
域名伺服器按照由高到低進行層次劃分:
注意: 一個域名伺服器所負責的范圍,稱為區
三、域名解析過程
域名解析的重要兩點:
以上兩點是域名解析的重要兩步。但是這並不是解析ip地址的完整過程,如果瀏覽器的緩存中有該域名對應的ip地址,就不需要向本地域名伺服器請求了等等。下面來看詳細過程:
例如要解析:www.example.com該域名的ip地址;
四、DNS安全和優化
1、dns安全問題
2、DNS優化
DNS解析是一個漫長的過程,那麼它的優化有哪些?
1、網頁端
用戶在請求請求某個鏈接之前,瀏覽器先嘗試解析該鏈接的域名再將其進行緩存。
可以這樣做:
(1) 在伺服器中響應設置X-DNS-Prefetch-Control的值為on啟動預解析
(2) 在HTML中,
(3) 在head中加入link標簽:
如
不過現在的Chrome瀏覽器會自動將當前頁面的所有帶href的dns都prefetch一遍。需要手動添加上面的link標簽的場景是:你後面訪問的域名不在當前頁面的所有鏈接中;
正確使用link標簽的姿勢:
域名收斂:建議將靜態資源只放在一個域名下面,可以減少DNS的請求
2、客戶端
HttpDNS
HttpDNS是使用HTTP協議向阿里雲的HTTPDNS伺服器的80埠直接進行請求,代替傳統的DNS協議向LDNS伺服器的53埠進行請求。從而可以繞過LDNS,可以避免運行商的域名劫持和調度不精準的問題;
五、HttpDNS介紹
總結:
網路優化的知識點很多,今天主要介紹了dns的知識點
下次繼續介紹Android網路優化的具體實現方案
C. Android Okhttp/Retrofit網路請求加解密實現方案
比較安全的方案應該是AES+RSA的加密方式。具體如下圖所示。
為什麼要這樣做呢?
1、RSA是非對稱加密,公鑰和私鑰分開,且公鑰可以公開,很適合網路數據傳輸場景。但RSA加密比較慢,據說比AES慢100倍,且對加密的數據長度也有限制。
2、AES是對稱加密,加密速度快,安全性高,但密鑰的保存是個問題,在網路數據傳輸的場景就很容易由於密鑰泄露造成安全隱患
3、所以,AES+RSA結合才更好,AES加密數據,且密鑰隨機生成,RSA用對方(伺服器)的公鑰加密隨機生成的AES密鑰。傳輸時要把密文,加密的AES密鑰和自己的公鑰傳給對方(伺服器)。對方(伺服器)接到數據後,用自己的私鑰解密AES密鑰,再拿AES密鑰解密數據得到明文。這樣就綜合了兩種加密體系的優點。
4、除上面說的外,還可以加簽名,即對傳輸的數據(加密前)先做個哈希,然後用自己的RSA私鑰對哈希簽名(對方拿到自己的公鑰可以驗簽),這樣可以驗證傳輸內容有沒有被修改過。
就java來說,加密的輸入和輸出都是位元組數組類型的,也就是二進制數據,網路傳輸或本地保存都需要重新編碼為字元串。推薦使用Base64。Android 有自帶的Base64實現,flag要選Base64.NO_WRAP,不然末尾會有換行影響服務端解碼。
Android中Base64加密
總而言之,這些不同語言都有實現庫,調用即可,關鍵是參數要一致,具體還需要和後台聯調一下。
rsa加解密的內容超長的問題解決
現在說到網路框架,應該毫無疑問是Retrofit了。上面說的加密方案說到底還是要在網路請求框架內加上,怎麼做入侵最小,怎麼做最方便才是重點。
1、坑定不能直接在介面調用層做加密,加參數,這樣每個介面都要修改,這是不可能的。
2、ConverterFactory處理,這也是網上可以搜到的很多文章的寫法,但我覺得還是有入侵。而且有點麻煩。
3、OkHttp添加攔截器,這種方法入侵最小(可以說沒有),實現呢也非常優雅。
下面的實現,網上也找不到多少可以參考的文章,但不得不說,OkHttp的封裝和設計真的很好用,所見即所得。看下源碼,就知道該怎麼用了,連文檔都不用查。
主要注意點:
0、和介面無關的新加的數據放在請求頭里。
1、該close的要close,不然會內存泄漏。
2、新舊Request和Response要區分好,新的要替換舊的去傳遞或返回。
3、要對response.code()做處理,只有在和後台約定好的返回碼下才走解密的邏輯,具體看自己的需求,不一定都是200。
D. Android面試筆記——HTTP/HTTPS
HTTP和HTTPS是面試常問的問題,內容比較多而且復雜,HTTPS裡面的細節很多,本文只是把主要的東西寫出來,想要弄懂HTTPS還是要多看幾篇博文,自己動手走一遍把各個攻擊的case搞明白。
HTTP 是超⽂本傳輸協議,也就是HyperText Transfer Protocol。
Host 欄位 :客戶端發送請求時,⽤來指定伺服器的域名。 Host: www..com
Content-Length 欄位 :伺服器在返回數據時,會有 Content-Length 欄位,表明本次回應的數據長度。 Content-Length: 1000
Connection 欄位 :Connection 欄位最常用於客戶端要求伺服器使⽤ TCP 持久連接,以便其他請求復⽤。 HTTP/1.1 版本的默認連接都是持久連接,但為了兼容⽼版本的 HTTP,需要指定 Connection ⾸部欄位的值為Keep-Alive 。
Content-Type 欄位 :Content-Type 欄位⽤於伺服器回應時,告訴客戶端,本次數據是什麼格式 。 Content-Type: text/html; charset=utf-8
Content-Encoding 欄位 :Content-Encoding 欄位說明數據的壓縮⽅法。表示伺服器返回的數據使用了什麼壓縮格式 。客戶端在請求時,⽤ Accept-Encoding 欄位說明自己可以接受哪些壓縮⽅法。 Accept-Encoding: gzip, deflate
下圖為訪問網路的返回欄位
HTTP/2 協議是基於 HTTPS 的,所以 HTTP/2 的安全性也是有保障的。
這都是基於 TCP 傳輸層的問題,所以 HTTP/3 把 HTTP 下層的 TCP 協議改成了 UDP 。
UDP 發生是不管順序,也不管丟包的,所以不會出現 HTTP/1.1 的隊頭阻塞 和 HTTP/2 的⼀個丟包全部重傳問題。
UDP 是不可靠傳輸的,但基於 UDP 的 QUIC 協議 可以實現類似 TCP 的可靠性傳輸。
HTTPS 采⽤的是 對稱加密和⾮對稱加密結合 的「混合加密」⽅式:
采⽤「混合加密」的⽅式的原因:
摘要演算法⽤來實現 完整性 ,能夠為數據⽣成獨⼀⽆⼆的「指紋」,⽤於校驗數據的完整性,解決了篡改的⻛險。
客戶端在發送明⽂之前會通過摘要演算法算出明文的「指紋」,發送的時候把「指紋 + 明文」⼀同加密成密文後,發送給伺服器,伺服器解密後,用相同的摘要演算法算出發送過來的明文,通過⽐較客戶端攜帶的「指紋」和當前算出的「指紋」做⽐較,若「指紋」相同,說明數據是完整的。
客戶端先向伺服器端索要公鑰,然後⽤公鑰加密信息,伺服器收到密文後,⽤⾃⼰的私鑰解密。這就存在些問題,如何保證公鑰不被篡改和信任度?
所以這⾥就需要藉助第三⽅權威機構 CA (數字證書認證機構),將伺服器公鑰放在數字證書(由數字證書認證機構頒發)中,只要證書是可信的,公鑰就是可信的。
通過數字證書的⽅式保證伺服器公鑰的身份,解決冒充的⻛險 。
證書簽名和驗證過程 :
兩種情況 :
E. 說說在 Android 中如何發送 HTTP 請求
客戶端會向伺服器發出一條 HTTP 請求,伺服器收到請求後會返回一些數據給客戶端,然後客戶端再對這些數據進行解析與處理。
可以使用 HttpURLConnection(官方推薦) 來發送 HTTP 請求。
布局文件:
活動類:
因為在 Android 中不允許在子線程中執行 UI 操作,所以我們通過 runOnUiThread 方法,切換為主線程,然後再更新 UI 元素。
最後記得聲明網路許可權哦:
OKHttp 是一個處理網路請求的開源項目,目前是 Android 最火熱的輕量級框架,由移動支付 Square 公司貢獻(該公司還貢獻了Picasso)。希望替代 HttpUrlConnection 和 Apache HttpClient。
首先引入 OKHttp 庫依賴:
然後點擊 Android Studio 右上角的 Sync Now,把庫真正載入進來。
修改活動類:
可以在 build() 方法之前連綴很多其他方法來豐富這個 Request 對象。
如果是 POST 請求,那麼需要構建 RequestBody 對象,形如:
修改活動類:
注意: new Thread(...) 之後需要執行 start() 才會啟動線程哦。
運行:
可以看出,OKHttp 比 HttpURLConnection 更強大:同一個網址,OKHttp 能夠正確地返回響應數據哦O(∩_∩)O哈哈~