前言
1. 網(wǎng)絡(luò)優(yōu)化概述
Android 網(wǎng)絡(luò)優(yōu)化方法主要講的就是線下網(wǎng)絡(luò)測試工具
、線上網(wǎng)絡(luò)監(jiān)控方案
、流量優(yōu)化方案
和質(zhì)量優(yōu)化方案
。
做網(wǎng)絡(luò)優(yōu)化時(shí),要考慮多個(gè)維度
、統(tǒng)計(jì)多種數(shù)據(jù)
,還要建立完善的監(jiān)控體系
。
常見的網(wǎng)絡(luò)測試工具有 Network Profiler
、Charles
和Stetho
。
常見的線上網(wǎng)絡(luò)監(jiān)控方案有OkHttp 的 EventListener
、NetworkStatsManager
和 TrafficStats
。
常見的流量優(yōu)化方案有數(shù)據(jù)緩存
、數(shù)據(jù)壓縮
和圖片壓縮
。
常見的質(zhì)量優(yōu)化方案有HttpDns 優(yōu)化
、協(xié)議版本優(yōu)化
以及資本優(yōu)化
。
考慮到多個(gè)維度
指的是做網(wǎng)絡(luò)優(yōu)化時(shí)要考慮流量維度
和質(zhì)量維度
,然后根據(jù)這兩個(gè)維度的數(shù)據(jù)減少流量消耗
以及提升請求速度
。
流量維度
指的是 App 在一段時(shí)間內(nèi)流量消耗的精準(zhǔn)度量,流量消耗可以區(qū)分為網(wǎng)絡(luò)類型
、前后臺
以及請求和響應(yīng)
等維度。客戶端要記錄這些數(shù)據(jù),這樣服務(wù)端就可以在需要的時(shí)候下發(fā)指令
控制客戶端上傳這些數(shù)據(jù),客戶端也可以在相關(guān)數(shù)據(jù)超過閾值后主動(dòng)上報(bào)
。
質(zhì)量維度
可以從請求時(shí)長
、請求成功率
和Top失敗接口
等數(shù)據(jù)上進(jìn)行區(qū)分,以便后續(xù)能快速定位和解決問題。
統(tǒng)計(jì)多種數(shù)據(jù)
指的是不能只統(tǒng)計(jì)線上流量消耗,還要統(tǒng)計(jì) App 使用時(shí)長
等數(shù)據(jù),結(jié)合多種數(shù)據(jù)來分析流量的消耗是否合理。
建立完善的監(jiān)控體系
,指的是做網(wǎng)絡(luò)優(yōu)化要監(jiān)控網(wǎng)絡(luò)請求成功率
和網(wǎng)絡(luò)請求異常
,比如流量消耗過多
、請求次數(shù)過多
以及下載文件過大
等異常。
網(wǎng)絡(luò)優(yōu)化要做線下測試
和線上監(jiān)控
,在做線下測試時(shí),要測試不同的網(wǎng)絡(luò)類型
下客戶端的使用體驗(yàn),比如切換 Wifi
和流量
,還要測試不同的網(wǎng)絡(luò)狀態(tài)
,比如弱網(wǎng)測試
和無網(wǎng)測試
。在做線下測試時(shí),要把測試周期拉長一些
,因?yàn)榭蛻舳酥杏械木W(wǎng)絡(luò)請求不是實(shí)時(shí)上報(bào)的,測試網(wǎng)絡(luò)的時(shí)候測久一點(diǎn)才能測出問題。另外一點(diǎn)就是有的 App 對安全性要求較高,切換網(wǎng)絡(luò)時(shí)可能會(huì)導(dǎo)致登錄態(tài)失效,這時(shí)要注意網(wǎng)絡(luò)請求取消時(shí)有沒有關(guān)閉加載彈窗
。
Network Profiler
是 Android Studio 提供的一個(gè)網(wǎng)絡(luò)分析工具,在 Network Profiler 中包含了連接視圖
、線程視圖
、請求概覽
、響應(yīng)信息
、請求信息
以及調(diào)用棧
等數(shù)據(jù)。在連接視圖中可以看到請求的內(nèi)容類型
、請求狀態(tài)
和請求耗時(shí)
等信息。
Charles
是一個(gè)抓包工具
,除了 Charles ,還有Fiddler
、Wireshark
、TcpDump
等抓包工具。Charles 支持斷點(diǎn)調(diào)試
、模擬數(shù)據(jù)
以及弱網(wǎng)模擬
功能。當(dāng)服務(wù)端的接口沒有開發(fā)完成,或者是我們想要驗(yàn)證一下不同的響應(yīng)數(shù)據(jù)在客戶端的處理是否正常時(shí),就可以使用模擬數(shù)據(jù)。
Stetho
是 Facebook 開源的一個(gè)應(yīng)用連接橋,使用 Stetho 可以在 Chrome 瀏覽器中查看 Android 設(shè)備和應(yīng)用相關(guān)的信息
,包含了網(wǎng)絡(luò)請求信息
、視圖查看、數(shù)據(jù)庫查看和命令行擴(kuò)展等功能。使用 Stetho,要在 OkHttp 客戶端添加 Stetho 攔截器,然后在 Chrome 中輸入 Chrome://inspect
,就能看到客戶端發(fā)起的請求信息以及接收到的響應(yīng)數(shù)據(jù)
。
服務(wù)端
要監(jiān)控不同緯度的請求耗時(shí)
、失敗率
以及 Top 失敗接口
,比如地域
和時(shí)間段
等維度,失敗率包括請求失敗
和業(yè)務(wù)失敗
。
客戶端
要監(jiān)控請求的每一個(gè)步驟的信息
,比如 DNS 解析時(shí)間
、連接建立時(shí)間
、請求時(shí)間
以及網(wǎng)絡(luò)包大小
等信息。客戶端還要監(jiān)控圖片請求,因?yàn)橐粡垐D片消耗的流量可能比 5 個(gè)甚至更多的接口的數(shù)據(jù)還要多。而且還要建立網(wǎng)絡(luò)容災(zāi)機(jī)制
,比如備用服務(wù)器分流
和在一定時(shí)間內(nèi)請求多次失敗時(shí),不再發(fā)起請求,并且加長重試時(shí)間
,避用戶量暴漲時(shí),服務(wù)器扛不住。
另外客戶端和服務(wù)端都要監(jiān)控網(wǎng)絡(luò)異常
,比如在服務(wù)端要判斷有沒有人在刷我們的服務(wù)器,有的話可以拒絕這些 IP 的服務(wù)
。而客戶端則要加上主動(dòng)預(yù)警功能,比如用戶下載了一個(gè)超過設(shè)定值的文件
,下載完后可以把這個(gè)結(jié)果上報(bào)給服務(wù)器,表明遇到了異常,需要進(jìn)一步確認(rèn)。
OkHttp 的 EventListener
能獲取到請求的各個(gè)步驟的耗時(shí)和數(shù)據(jù),提供了DNS 解析
、建立連接
、建立 HTTPS 連接
、寫入請求首部
、寫入請求體
等事件的開始
、結(jié)束
和失敗
的回調(diào)方法。而且 EventListener 還能和 Glide
搭配使用,以獲取圖片請求的各個(gè)步驟的數(shù)據(jù)。
另外就是 OkHttp 默認(rèn)單個(gè)主機(jī)能并發(fā)執(zhí)行的請求最多為 5 個(gè)
,如果我們做了域名收斂,請求的都是同一個(gè)主機(jī),就可以把 OkHttpClient 的 maxRequestsPerHost
的值設(shè)大一些。
NetworkStatsManager
是一個(gè)流量統(tǒng)計(jì)管理器
,可以獲取某個(gè)時(shí)間段
和不同網(wǎng)絡(luò)類型
的流量消耗,但是需要用戶開啟查看使用情況
權(quán)限,用戶體驗(yàn)較差。
做流量優(yōu)化
時(shí)要先設(shè)定預(yù)期
以及和競品對比
,比如預(yù)計(jì)一個(gè)新功能在用戶使用后,單次消耗流量為 300kb,但是上線后超過 300kb,這時(shí)就要看下找出并解決這個(gè)問題。另外還要和競品對比,比如同樣是發(fā)布評論的主流程,相同的評論和相同的圖片,和競品對比之下的流量消耗
是多少。
TrafficiStats
也是一個(gè)流量數(shù)據(jù)統(tǒng)計(jì)工具,缺點(diǎn)是只能統(tǒng)計(jì)到手機(jī)上次重啟后到現(xiàn)在的流量消耗。
在我們記錄了網(wǎng)絡(luò)相關(guān)的日志后,在上報(bào)性能日志
時(shí),大致要做的事情有四件,分別是App 啟動(dòng)時(shí)執(zhí)行一個(gè)后臺任務(wù)
、每隔一段時(shí)間(如 30 秒)就獲取一次網(wǎng)絡(luò)數(shù)據(jù)
、給數(shù)據(jù)加上標(biāo)志
,比如區(qū)分前后臺的標(biāo)志、在合適的時(shí)機(jī)把數(shù)據(jù)上報(bào)到 APM 后臺
,合適的時(shí)機(jī)指的是用戶反饋時(shí)
、達(dá)到閾值
或處于 WiFi 網(wǎng)絡(luò)下
這樣的情況下,這樣性能日志消耗的流量就不會(huì)對用戶造成大的影響。
數(shù)據(jù)緩存
可以分為 OkHttp 的 Cache
和 無網(wǎng)攔截器
兩種方案,我們項(xiàng)目中一般會(huì)有一些對實(shí)時(shí)性要求不高的接口,比如省市區(qū)列表,這時(shí)就可以考慮緩存這些接口的數(shù)據(jù)。
Cache
是 OkHttp 的緩存攔截器要用到的一個(gè)類,不設(shè)置的話 OkHttp 默認(rèn)是不會(huì)緩存響應(yīng)數(shù)據(jù)的。Cache 內(nèi)部使用了 DiskLruCache
,創(chuàng)建 Cache 只需要指定緩存目錄
和緩存大小
,然后設(shè)置給 OkHttpClient 就可以了。使用 Cache 要注意的是,默認(rèn)緩存攔截器只會(huì)緩存 GET 和 HEAD 等獲取資源的請求方法
的響應(yīng)。
無網(wǎng)攔截器
指的是自定義一個(gè)實(shí)現(xiàn)了 Interceptor 接口的類,然后在網(wǎng)絡(luò)不可用時(shí),把強(qiáng)制緩存策略 FORCE_CACHE
傳到 Request 的 Builder 的 cacheControl()
方法中就可以了。做數(shù)據(jù)緩存時(shí),數(shù)據(jù)要加上過期時(shí)間
或版本號
,每次請求的時(shí)候客戶端要判斷一下數(shù)據(jù)有沒有過期,或者是判斷一下數(shù)據(jù)的版本號有沒有更新。
第二個(gè)流量優(yōu)化方案是數(shù)據(jù)壓縮
,具體有 開啟 Gzip 壓縮
、壓縮請求頭
以及合并請求
三個(gè)方案。
開啟 Gzip 壓縮
就是把內(nèi)容編碼 Content-Encoding
首部設(shè)為 gzip
,如果我們的網(wǎng)絡(luò)框架用的是 OkHttp ,那么在沒有設(shè)置 Content-Encoding 請求頭的情況下,OkHttp 默認(rèn)會(huì)我們把 Content-Encodingshe 設(shè)置為 gzip。
壓縮請求頭
指的是在請求頭不變的情況下,讓服務(wù)端緩存請求頭,在需要請求頭的某些信息時(shí),根據(jù)之前的請求頭的 MD5 從之前的緩存中取。
合并請求
指的是把可以合并的請求合并起來,因?yàn)槊恳粋€(gè)網(wǎng)絡(luò)請求頭會(huì)有冗余信息。
第三個(gè)流量優(yōu)化方案就是圖片壓縮
,具體的有縮略圖
、WebP
以及 Luban
。
縮略圖
指的是在列表中優(yōu)先使用縮略圖,因?yàn)樵诹斜碇兄苯诱故驹瓐D沒有意義。
WebP
是一種提供了有損壓縮和無損壓縮的圖片格式,能把圖片體積減少 50% 甚至更多。
Luban
是一個(gè)圖片壓縮工具,模仿了微信朋友圈的壓縮策略,能把圖片體積減少 95% 甚至更多,缺點(diǎn)是有損的,就像朋友圈的圖片多少有點(diǎn)模糊一樣。
網(wǎng)絡(luò)請求質(zhì)量優(yōu)化的兩個(gè)指標(biāo)是網(wǎng)絡(luò)請求成功率
和網(wǎng)絡(luò)請求速度
,網(wǎng)絡(luò)請求質(zhì)量優(yōu)化常見的三個(gè)方案是 使用 HttpDNS
、升級 HTTP 版本
以及砸錢
。
使用 HttpDNS
可以繞過運(yùn)營商域名解析過程
,HttpDNS 使用的不是傳統(tǒng)的 DNS 協(xié)議,而是使用 HTTP 協(xié)議,使用 HttpDNS 的好處是防劫持
以及提升訪問速度
。
升級 HTTP 版本
指的是把 HTTP 協(xié)議升級為 HTTP/2
,HTTP/2 是一個(gè)二進(jìn)制協(xié)議,最大的改進(jìn)就是客戶端和服務(wù)器可以同時(shí)發(fā)送多個(gè)請求和響應(yīng)
,不用像 HTTP/1.1 一樣按順序請求。
砸錢
指的是花錢做 CDN 加速
、提高帶寬
以及動(dòng)靜資源分離
。
1. 網(wǎng)絡(luò)優(yōu)化的三個(gè)要點(diǎn)
網(wǎng)絡(luò)優(yōu)化的三個(gè)要點(diǎn)分別是多維
、精準(zhǔn)
和監(jiān)控
。
多維指的是網(wǎng)絡(luò)優(yōu)化要考慮多個(gè)維度
,一談到網(wǎng)絡(luò)優(yōu)化,大部分人首先想到的就是流量消耗,但是實(shí)際上流量消耗多少只是網(wǎng)絡(luò)優(yōu)化的其中一個(gè)維度。
只對流量消耗一個(gè)維度進(jìn)行優(yōu)化是不夠的,甚至有的團(tuán)隊(duì)即便在流量優(yōu)化上也沒有做好,比如對于網(wǎng)絡(luò)流量的消耗統(tǒng)計(jì)不夠全面和精確。
精準(zhǔn)指的是在做網(wǎng)絡(luò)流量統(tǒng)計(jì)時(shí)的度量要精準(zhǔn)
,如果只是獲取了具體消耗了多少的值,對于我們定位和解決問題是沒有太大的幫助,因?yàn)檫@個(gè)值只能表明用戶使用了多少流量。否則如果線上用戶反饋 App 消耗流量較多,但是我們不知道這個(gè)用戶總共使用了 App 多長時(shí)間的話,那就不好定位問題所在,如果用戶使用 App 的時(shí)間比較長,那消耗流量多一些很可能是正常的。又比如用戶反饋 App 在后臺消耗流量比較多,但是我們只統(tǒng)計(jì)了整體的值,那就無法斷定 App 在后臺運(yùn)行時(shí)到底消耗了多少流量。
監(jiān)控指的是要建設(shè)全面且完善的網(wǎng)絡(luò)監(jiān)控體系
,不能只監(jiān)控一個(gè)指標(biāo),假如只監(jiān)控網(wǎng)絡(luò)請求成功率,那我們就只能知道用戶大概的網(wǎng)絡(luò)使用情況,這種粗粒度的監(jiān)控沒辦法幫助我們找出并解決問題的根源。比如線上用戶使用了某個(gè)功能使用了 1000 次,然后出現(xiàn)了 1 次異常,而且用戶點(diǎn)擊重試后就恢復(fù)正常了。這樣單從數(shù)據(jù)上來看的話,網(wǎng)絡(luò)請求的成功率還是比較高的,但是只通過成功率一個(gè)值是無法知道這一次異常出現(xiàn)的原因,也就無法避免后續(xù)出現(xiàn)這類異常。
2. 網(wǎng)絡(luò)優(yōu)化的兩個(gè)維度
網(wǎng)絡(luò)優(yōu)化的兩個(gè)維度分別是流量維度
和質(zhì)量維度
。
流量維度指的是 App 在一段時(shí)間內(nèi)流量消耗的精準(zhǔn)度量
,流量消耗大不僅對用戶有影響,對公司的運(yùn)營成本也有影響,比如帶寬
、服務(wù)器數(shù)量
、CDN
等方面的開支,而且網(wǎng)絡(luò)請求密集對手機(jī)耗電量
也有一定的影響,在流量維度上,我們要區(qū)分類型
、監(jiān)控異常
并上報(bào)日志
。
-
區(qū)分類型
區(qū)分類型就是要知道用戶在
不同網(wǎng)絡(luò)類型
(流量、WiFi)下的流量消耗、區(qū)分 App 在前臺
和在后臺
時(shí)的流量消耗。只有積累了不同維度的數(shù)據(jù),才能快速斷定和解決問題。 -
監(jiān)控異常
異常監(jiān)控就是我們不僅要知道用戶的
流量消耗均值
,還要知道線上用戶消耗流量的異常率。這里的異常分為流量消耗過多
、請求次數(shù)過多
以及下載文件過大
三種。 -
上報(bào)日志
最理想的情況就是我們對所有的網(wǎng)絡(luò)請求,在本地都有一個(gè)完整的監(jiān)控,每一個(gè)請求的
Request
和Response
相關(guān)的所有信息都全部記錄下來。服務(wù)端可以下發(fā)指令控制客戶端上傳這些數(shù)據(jù),客戶端也可以在相關(guān)數(shù)據(jù)超過閾值后主動(dòng)上報(bào)。
2. 質(zhì)量維度
網(wǎng)絡(luò)請求的質(zhì)量也非常關(guān)鍵,它直接對應(yīng)了用戶的真實(shí)體驗(yàn),如果網(wǎng)絡(luò)請求速度慢或請求成功率比較低,都會(huì)導(dǎo)致不好的用戶體驗(yàn)。
對于網(wǎng)絡(luò)請求質(zhì)量的監(jiān)控,可以從請求時(shí)長
、請求成功率
、失敗率
、Top 失敗接口
等維度進(jìn)行區(qū)分,以便后續(xù)能快速定位和解決問題。
3. 網(wǎng)絡(luò)優(yōu)化的兩個(gè)誤區(qū)
做網(wǎng)絡(luò)優(yōu)化時(shí)不能只關(guān)注流量消耗,忽視了其他維度
,做網(wǎng)絡(luò)監(jiān)控時(shí)不能只關(guān)注均值和整體的數(shù)據(jù),忽略了個(gè)體的數(shù)據(jù)
。
比如前面提到的請求成功率的例子,從整體上來看成功率非常高,但是這種數(shù)據(jù)無法幫助我們改善單次請求。
1. 三個(gè)線下測試工具
常用的線下網(wǎng)絡(luò)測試工具有 Network Profiler
、Charles
、Stetho
這 3 個(gè),Network Profiler 是 Android Studio 提供的網(wǎng)絡(luò)分析工具,能顯示實(shí)時(shí)的網(wǎng)絡(luò)活動(dòng),比如發(fā)送網(wǎng)絡(luò)請求、接收的數(shù)據(jù)以及連接數(shù)。Charles 是抓包工具,Stetho 是 Facebook 開源的一個(gè)網(wǎng)絡(luò)分析工具。
1.1 線下測試注意事項(xiàng)
線下測試是為了把問題盡可能在上線前暴露出來
,在線下測試環(huán)節(jié),我們要注意網(wǎng)絡(luò)切換
、弱網(wǎng)/無網(wǎng)測試
、請求是否有誤
以及放長測試周期
等 4 點(diǎn)。
在做線下測試時(shí),要切換不同的網(wǎng)絡(luò),比如 Wifi 和流量,而且還要做弱網(wǎng)測試和無望測試,保證我們的 App 不同的網(wǎng)絡(luò)類型以及不同的網(wǎng)絡(luò)狀態(tài)下都有著很好的體驗(yàn)。
在請求失敗時(shí),首先要看接口請求是否有誤,比如傳了過多的參數(shù)或傳參不正確
。
在做線下流量消耗測試時(shí),要把周期拖長一些
,因?yàn)?C 端的 App 到了穩(wěn)定期后,用戶可能高達(dá)幾千萬甚至更多,這時(shí) App 功能一般是非常復(fù)雜的,而且包含了其他功能,比如性能監(jiān)控等。這些功能的網(wǎng)絡(luò)請求往往不是實(shí)時(shí)上報(bào)的,所以我們在做流量消耗測試的時(shí)候,周期要拉長,簡單測 10 分鐘很可能測不出問題。
有的 App 對安全性要求比較高,如果突然切換網(wǎng)絡(luò)狀態(tài),會(huì)導(dǎo)致用戶的登錄態(tài)失效
,也就是要用戶重新登錄。這時(shí)我們要注意重新登錄后 App 會(huì)不會(huì)重新回到之前的流程中,不能中斷用戶正在進(jìn)行的流程
。
在弱網(wǎng)或無網(wǎng)狀態(tài)下,要測試加載彈窗是否會(huì)停止
,如果不注意的話,在無網(wǎng)的情況下,應(yīng)用的請求失敗了,但是卻沒有關(guān)閉 Loading 彈窗,影響了用戶的其他操作。如果對無網(wǎng)狀態(tài)重視不足,就測不出這樣的 Bug 。
1.1 Network Profiler
Network Profiler 是 AS 自帶的網(wǎng)絡(luò)分析工具,它能顯示實(shí)時(shí)網(wǎng)絡(luò)活動(dòng),比如發(fā)送網(wǎng)絡(luò)請求、接收的數(shù)據(jù)以及連接數(shù)等。
在 Profiler 中有一個(gè)連接視圖(Connection View)和線程視圖(ThreadView),連接視圖中包含了連接的信息,比如請求時(shí)間、響應(yīng)數(shù)據(jù)、請求數(shù)據(jù)以及調(diào)用棧,線程視圖中包含了不同的網(wǎng)絡(luò)請求線程的執(zhí)行時(shí)間線
。
首先點(diǎn)擊 run 旁邊的 Profile 'app'
按鈕開始分析。
然后在 Profiler 下方可以看到連接視圖
(Connection View),右側(cè)則是某個(gè)連接的信息,包括請求時(shí)間、響應(yīng)、請求以及調(diào)用棧。
下面是線程視圖。
Request 面板中包含了 User-Agent 和 Connection 等首部信息,以及請求體。
Response 面板中包含了響應(yīng)體和響應(yīng)首部數(shù)據(jù)。
Call Stack(調(diào)用棧)中包含了網(wǎng)絡(luò)請求的調(diào)用棧。
1.2 Charles
常見的抓包工具有 Charles
、Fiddler
、Wireshark
和 TcpDump
四個(gè),其中 Charles 是用 Java 開發(fā)的,在 Mac 上使用得比較多,F(xiàn)iddler 在 Windows 上使用得比較多。下面我們重點(diǎn)來看下 Charles 的使用, Charles 支持斷點(diǎn)
、模擬數(shù)據(jù)
以及弱網(wǎng)模擬
等功能。
1.2.1 基礎(chǔ)用法
首先在官網(wǎng)上下載 Charles
- 打開 Charles 的代理功能
- 查看電腦 IP 地址
vboxnet2 是虛擬機(jī) Virtual Box 的地址。
- 設(shè)置 WiFi 代理地址
-
同意連接
設(shè)置代理后,當(dāng)我們在設(shè)備上訪問網(wǎng)絡(luò)時(shí),Charles 會(huì)彈出一個(gè)是否允許連接的彈窗,我們點(diǎn)擊允許(Allow)即可。
-
安裝證書
如果是抓取 HTTPS ,那就要安裝證書,否則請求和響應(yīng)信息都會(huì)是亂碼。
搜索 charles 并選擇始終信任。
- 安裝手機(jī)證書
選擇安裝證書在移動(dòng)設(shè)備后,Charles 會(huì)彈出一個(gè)代理提示,讓我們?nèi)?chls.pro/ssl 下載并安裝證書。
我已經(jīng)下載過了,所以這里提示的是重新下載,下載后點(diǎn)擊安裝即可。
-
添加主機(jī)
然后在 SSL Proxying Settings 中添加我們要抓包的主機(jī)地址,這里可以使用通配符 *.* 。
- 添加網(wǎng)絡(luò)安全配置
在 res/xml 下添加 network_security_config.xml
然后在 Manifest 的 Application 標(biāo)簽中添加該配置。
然后就可以看到抓包結(jié)果了。
1.2.2 斷點(diǎn)
我們右鍵點(diǎn)擊任意一個(gè)請求后,就可以看到斷點(diǎn)(Breakpoints)選項(xiàng)。
如果想要查看已經(jīng)添加的斷點(diǎn)列表,可以在 Proxy->Breakpoint Settings 中查看。
如果想只對 Request 或 只對 Response 斷點(diǎn),可以點(diǎn)擊斷點(diǎn),然后在編輯斷點(diǎn)彈窗中設(shè)置。
下面看下進(jìn)入斷點(diǎn)后的界面,當(dāng)我們再次發(fā)起打了斷點(diǎn)的請求時(shí),可以看到斷點(diǎn)界面。
當(dāng)我們點(diǎn)擊 Edit Request 標(biāo)簽后,我們可以在這里編輯請求的信息。
當(dāng)我把 version 改為 3 后,可以看到請求中的參數(shù)改成了 3 。
1.2.3 模擬數(shù)據(jù)
Charles 有一個(gè)叫 Map Local 的功能,Map Local 多用于服務(wù)端接口未開發(fā)完畢,只定義了協(xié)議時(shí)的情況。
我們可以用 Map Local 模擬自己想要的假數(shù)據(jù),可以自由模擬各種臟數(shù)據(jù),這樣就算服務(wù)端開發(fā)進(jìn)度比較慢,不會(huì)影響客戶端的開發(fā)進(jìn)度,除了 Map Local,也可以使用 MockK 等用于測試的模擬框架來實(shí)現(xiàn)功能。
下面我們來看下怎么使用 Map Local。
然后右鍵點(diǎn)擊請求,彈出菜單的最下方就是 Map Local。
點(diǎn)擊 Map Local 后,可以看到 Edit Mapping 彈窗。
原始的響應(yīng)是下面這樣的。
然后建立一個(gè) maplocal 文件,把 group 里的 count 改成 20 。
當(dāng)客戶端再次發(fā)起相同的請求時(shí),就可以看到 group 的 count 的值變成了 20 。
如果想關(guān)閉 Map Local,可以點(diǎn)擊 Tools -> Map Local,然后把 Enable Map Local 的勾選取消掉。
1.2.4 弱網(wǎng)模擬
Charles 支持弱網(wǎng)模擬功能,點(diǎn)擊 Proxy -> Start throtting。
勾選 Enable Throttling 后,就開啟了限流功能,限流功能支持下面幾個(gè)選項(xiàng)。
預(yù)設(shè)值(Throttle preset)
帶寬(Bandwidth)
利用率(Utilisation)
往返延遲(Round-trip-latency)
MTU(傳輸 TCP 包的最大尺寸)
可靠性(Reliability,丟包)
穩(wěn)定性(Stability,抖動(dòng))
-
不穩(wěn)定質(zhì)量范圍(Unstable quality range)
主要針對 Statbility 中設(shè)置的范圍;
下面是一些常用的預(yù)設(shè)值。
下面這個(gè)請求,在限流前的總處理時(shí)間是 177 毫秒。
當(dāng)開啟了弱網(wǎng)模擬后,變成了 9.3 秒。
1.3 Stetho
看完了 Charles,下面我們來學(xué)習(xí)下 Stetho,Stetho 是一個(gè)強(qiáng)大的應(yīng)用連接橋,可以連接 Android 和 Chrome,有網(wǎng)絡(luò)監(jiān)控、視圖查看、數(shù)據(jù)庫查看、命令行擴(kuò)展等功能,下面來看下它的用法。
首先添加依賴,這個(gè)庫是 Stetho 針對 OkHttp 的實(shí)現(xiàn)。
// Android 應(yīng)用調(diào)試工具
implementation 'com.facebook.stetho:stetho-okhttp3:1.5.1'
然后初始化并添加攔截器。
在 Chrome 中輸入 Chrome://inspect ,然后就可以看到設(shè)備上的 Chrome 已經(jīng)打開的標(biāo)簽,下方就是打開了 Stetho 調(diào)試的 App。
點(diǎn)擊豆芽下面的 inspect 后,就可以看到 DevTools,點(diǎn)擊 DevTools 中的 Network 標(biāo)簽,就可以看到 App 發(fā)出的請求和接收到的響應(yīng)等數(shù)據(jù)。
由于 Stetho 的抓包功能沒那么強(qiáng)大,所以一般情況下不會(huì)用 Stetho 作為抓包工具。
2. 線上監(jiān)控的三個(gè)要點(diǎn)
在看獲取網(wǎng)絡(luò)流量前,我們先來看下線上監(jiān)控的三個(gè)要點(diǎn):服務(wù)端監(jiān)控
、客戶端監(jiān)控
以及異常監(jiān)控
。
2.1 服務(wù)端監(jiān)控
服務(wù)端要監(jiān)控不同維度
的網(wǎng)絡(luò)請求耗時(shí)
、失敗率
以及 Top 失敗接口
,,比如地域
、時(shí)間段
、版本
和機(jī)型
。
而失敗率要分為請求失敗
和業(yè)務(wù)失敗
兩個(gè)維度,請求失敗就是網(wǎng)絡(luò)請求失敗,業(yè)務(wù)失敗指的是網(wǎng)絡(luò)請求雖然成功了,但是用戶沒有拿到自己需要的數(shù)據(jù),統(tǒng)計(jì)多個(gè)維度的失敗率才能更敏銳地感知線上的異常波動(dòng)
。
在 APM 后臺,最好統(tǒng)計(jì)一下在一段時(shí)間內(nèi),比如 1 天或 1 周內(nèi),排行 Top 的失敗接口或異常接口
,每隔一段時(shí)間就進(jìn)行一次統(tǒng)計(jì)
,這樣就能知道哪些接口不穩(wěn)定,以便進(jìn)行針對性的優(yōu)化。
2.2 客戶端監(jiān)控
客戶端監(jiān)控比服務(wù)端監(jiān)控更關(guān)鍵
,在客戶端的監(jiān)控更全面,能拿到更多的數(shù)據(jù),客戶端在線上要監(jiān)控請求的每一個(gè)步驟的信息
、圖片請求
以及建立網(wǎng)絡(luò)容災(zāi)機(jī)制
,要統(tǒng)計(jì)請求耗時(shí)
、成功率
、錯(cuò)誤碼
以及圖片加載每一步的耗時(shí)
。雖然圖片加載耗時(shí)和成功率在服務(wù)端也可以統(tǒng)計(jì),但是服務(wù)端拿到的數(shù)據(jù)不完整,因?yàn)橛械恼埱髩焊?code>沒到服務(wù)端就失敗了。而且服務(wù)端傳過來的數(shù)據(jù)加上網(wǎng)絡(luò)通道的延遲時(shí)間,肯定比服務(wù)端統(tǒng)計(jì)到的時(shí)間要長,所以我們要在客戶端也加上統(tǒng)計(jì)。
客戶端能拿到請求的每一個(gè)步驟的信息,包括 DNS 解析時(shí)間
、建立連接的時(shí)間
、請求時(shí)間
以及網(wǎng)絡(luò)包大小
等信息。同時(shí)我們可以記錄用戶每一次網(wǎng)絡(luò)請求的操作
,比如具體請求了哪些接口
,請求是否成功
以及請求失敗的原因
,這些信息我們都能作為監(jiān)控的基礎(chǔ)信息傳給 APM 服務(wù)端
,在后面會(huì)介紹如何監(jiān)控網(wǎng)絡(luò)請求的每一步。
客戶端還要監(jiān)控圖片請求
,一張圖片消耗的流量可能比 5 個(gè)甚至更多接口的數(shù)據(jù)還要多。
客戶端要建立網(wǎng)絡(luò)容災(zāi)機(jī)制
,假如某一天我們的用戶量突然暴漲,服務(wù)器可能扛不住這個(gè)壓力,對于服務(wù)端來說,可以用備用服務(wù)器分流
,避免把主服務(wù)器搞垮。而客戶端也可以做一個(gè)策略,在一定時(shí)間內(nèi)網(wǎng)絡(luò)請求失敗多次時(shí),就不再進(jìn)行網(wǎng)絡(luò)請求
。
2.3 異常監(jiān)控
異常監(jiān)控的目的,是提升我們對異常的感知靈敏度,而不是被動(dòng)等待用戶反饋。
1. 服務(wù)器防刷
在服務(wù)端,我們要判斷是不是有人在刷我們的服務(wù)器,也就是惡意攻擊,如果檢測到有人在刷服務(wù)器,我們可以鎖定 IP 拒絕這些 IP 訪問。
2. 異常兜底
在客戶端,我們可以加上主動(dòng)預(yù)警能力
,比如用戶下載了一個(gè)超過設(shè)定值的文件
,客戶端在下載后就可以把這個(gè)結(jié)果上報(bào)給服務(wù)器,表明現(xiàn)在遇到了異常,需要研發(fā)同學(xué)進(jìn)一步確認(rèn)。
在一些場景下,服務(wù)端可能出現(xiàn)流量過多扛不住的情況,這時(shí)客戶端可以做一個(gè)兜底策略,如果在一定時(shí)間內(nèi),比如 30 秒內(nèi),接口請求連續(xù)失敗 5 次,那就不允許持續(xù)訪問,同時(shí)把重試時(shí)間設(shè)長一些
。
3. 單點(diǎn)問題追查
假如線上用戶反饋 App 消耗流量過多,或者是在后臺時(shí)消耗流量較多,我們都可以具體分析用戶下的網(wǎng)絡(luò)請求日志,以及下發(fā)命令查看具體時(shí)間段的流量消耗。
3. 三個(gè)線上監(jiān)控方案
有的問題只在線上出現(xiàn),在線下發(fā)現(xiàn)不了,比如在線下測試某個(gè)版本的時(shí)候,H5 包不一定是新版本,但是到了線上后,部分用戶可能會(huì)被命中,然后下發(fā)了新版本的包。
而這些包如果沒有經(jīng)過壓縮,這樣的異常就無法在線下的測試環(huán)境中全部發(fā)現(xiàn),只能通過線上監(jiān)控發(fā)現(xiàn)。
下面我們來看下三個(gè)線上監(jiān)控方案:
- OkHttp 事件監(jiān)聽器
- NetworkStatsManager
- TrafficStats
這三個(gè)方案中 OkHttp 事件監(jiān)聽器能獲取到的數(shù)據(jù)最細(xì)致,也最實(shí)用,而 NetworkStatsManager 和 TrafficStats 主要是用于獲取流量消耗。
3.1 OkHttp 事件監(jiān)聽器
3.1.1 自定義事件監(jiān)聽器
下面我們來看下如何結(jié)合 OkHttp 獲取網(wǎng)絡(luò)請求質(zhì)量數(shù)據(jù),OkHttp 給我們留了一個(gè)事件監(jiān)聽器 EventListener 回調(diào),我們可以自己實(shí)現(xiàn)這個(gè)監(jiān)聽器,監(jiān)聽每一次的請求。
首先定義一個(gè) Model,用于存儲(chǔ)請求和響應(yīng)相關(guān)數(shù)據(jù),這里為了演示只加上了部分?jǐn)?shù)據(jù),大家可以根據(jù)自己的需要制定自己的 Model。
創(chuàng)建 OkHttpEventListener,并重寫我們想要的方法,這里為了演示只重寫了部分方法,OkHttpEventListener 提供了非常多的方法,大家可以根據(jù)自己的需要重寫。
然后設(shè)置 OkHttpClient 的事件監(jiān)聽器工廠 。
設(shè)置了工廠后,我們就能拿到每個(gè)網(wǎng)絡(luò)請求的每一步的耗時(shí)和相關(guān)數(shù)據(jù),包括 DNS 解析事件、請求事件、響應(yīng)時(shí)間、響應(yīng)字節(jié)數(shù)等數(shù)據(jù),這些都是我們做線上監(jiān)控必不可少的數(shù)據(jù)支撐。
3.1.2 自定義 GlideModule
自定義 GlideModue 是為了監(jiān)控圖片加載的過程,下面我們來看下怎么監(jiān)控 Glide 加載圖片過程的耗時(shí)和相關(guān)數(shù)據(jù)。
首先添加插件和依賴。
// Kotlin 插件
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
android {
// ...
}
dependencies {
// ...
// 圖片加載
kapt 'com.github.bumptech.glide:compiler:4.11.0'
implementation 'com.github.bumptech.glide:glide:4.11.0'
implementation "com.github.bumptech.glide:okhttp3-integration:4.11.0"
}
然后定義一個(gè) GlideModue,替換 ModelLoader 工廠。
點(diǎn)擊 Build -> Clean Project,再點(diǎn)擊 Build -> Make Project。
然后把 Glide 替換為 GlideApp。
然后我們就可以在自定義的事件監(jiān)聽器里監(jiān)聽到圖片加載過程的耗時(shí)和大小了。
3.1.3 OkHttp 最大并發(fā)請求數(shù)
關(guān)于請求的頻率,我們可以看下 OkHttp 默認(rèn)的請求池,在 OkHttp 的 Dispatcher 分發(fā)器中,有一個(gè) executeService 線程池,它的核心池大小為 0 ,最大值是整型的最大值。
但這并不是意味著通過 OkHttp 發(fā)送的網(wǎng)絡(luò)請求可以并發(fā)無數(shù)個(gè),對于 IO 密集型任務(wù)我們可以多發(fā)送一些,因?yàn)檫@類任務(wù)對 CPU 消耗不大,但是我們還是要注意,單個(gè) App 能創(chuàng)建的線程數(shù)也是有上限的。
雖然線程池大小沒有限制,但是在請求執(zhí)行前會(huì)在 promoteAndExecute() 方法中判斷是否已超出最大請求數(shù)量,而判斷的依據(jù)就是 maxRequests(最大并發(fā)請求數(shù)) 和 maxRequestsPerHost(單個(gè)主機(jī)最大并發(fā)請求數(shù))。
之所以要設(shè)置單個(gè)主機(jī)最多能并發(fā)執(zhí)行的請求數(shù)量,防止某個(gè)域名的請求過多,導(dǎo)致其他域名沒有機(jī)會(huì)執(zhí)行。
假如我們的項(xiàng)目中已經(jīng)做了域名收斂,只有一個(gè)域名,就可以增加單個(gè)域名下的數(shù)量限制,這樣同時(shí)執(zhí)行的網(wǎng)絡(luò)請求的數(shù)量就能多一些。
3.1.4 區(qū)分前后臺流量
之所以要在日志中區(qū)分前后臺流量,是因?yàn)楹芏嘤脩魰?huì)擔(dān)心 App 一直在后臺消耗流量,如果粗粒度地只獲取流量數(shù)據(jù),是不知道這些流量有多少是 App 在后臺運(yùn)行時(shí)消耗的,這樣的話不要說解決用戶反饋的問題,就連定位問題都定位不了。
通過 ProcessLifecycleOwner 給請求數(shù)據(jù)打上標(biāo)志,這樣我們就可以分別知道用戶在前臺和后臺的流量消耗,而且用戶反饋時(shí),我們就可以自己查看該用戶的流量消耗統(tǒng)計(jì),判斷是否存在異常。
這個(gè)方案存在的不足,就是當(dāng)用戶在 30 秒內(nèi)切換 App 的前后臺狀態(tài)時(shí),會(huì)有一定的誤差,但是這個(gè)誤差是在可接受范圍內(nèi)的。
使用這種方式,再結(jié)合 APM 后臺設(shè)置的閾值,讓客戶端在流量消耗達(dá)到閾值后自動(dòng)上報(bào),從而實(shí)現(xiàn)更精準(zhǔn)的流量消耗監(jiān)控。
下面我們來看下怎么使用 ProcessLifecycleOwner 。
1. 添加依賴
有很多 AndroidX 的庫自帶了 process 庫,大家可以在 External Libraries 中看下有沒有,有就不用添加下面這個(gè)庫了。
dependencies {
// AndroidX Lifecycle 進(jìn)程
implementation 'androidx.lifecycle:lifecycle-process:2.2.0'
}
2. 定義應(yīng)用生命周期觀察者
3. 添加觀察者
4. ProcessLifecycleOwner 原理
在 ProcessLifecycleOwner 出來之前,我們自己也可以通過計(jì)數(shù)的方式來判斷是不是所有 Activity 都隱藏了,是的話則斷定為應(yīng)用隱藏了。
而 ProcessLifecycleOwner 只是幫我們把這件事情做了,在 ProcessLifecycleOwner 的初始化方法 ini 中調(diào)用了 attach 方法,而 attach 方法中實(shí)現(xiàn)了 Activity 生命周期回調(diào)接口。
以 onStart 為例,ProcessLifecycleOwner 中有一個(gè) mStartdCounter,每次有一個(gè) Activity 的 onStart 方法被調(diào)用了,mStartdCounter 就會(huì)加 1,如果 mStartCounter 等于 1 ,則表明應(yīng)用啟動(dòng)了,然后 ProcessLifecycleOnwer 就會(huì)通過 mRegistry 調(diào)用我們自定義的 onAppForeground 方法。
3.1.5 四步上傳數(shù)據(jù)
下面是上報(bào)性能日志的大概的四個(gè)步驟。
-
后臺任務(wù)
在 App 啟動(dòng)時(shí),執(zhí)行一個(gè)后臺任務(wù);
-
間隔統(tǒng)計(jì)
這個(gè)任務(wù)每隔一段時(shí)間(如 30 秒內(nèi))就獲取網(wǎng)絡(luò)數(shù)據(jù);
-
自定數(shù)據(jù)
自己維護(hù)一份數(shù)據(jù)統(tǒng)計(jì),給數(shù)據(jù)加上標(biāo)志,記住用戶在前后臺的流量消耗總量;
-
上報(bào)數(shù)據(jù)
在合適的時(shí)機(jī)(如用戶反饋、達(dá)到閾值、處于 WiFi 網(wǎng)絡(luò)下)把數(shù)據(jù)上報(bào)到 APM 后臺,這樣對用戶的流量就不會(huì)造成影響,而且數(shù)據(jù)也能作為流量治理依據(jù);
3.2 NetworkStatsManager
如果我們不使用 OkHttp 的 EventListener ,而是用了 NetworkStatsManager,那么當(dāng)用戶反饋說某個(gè)時(shí)間段內(nèi)的流量消耗較多時(shí),我們也可以在后臺給客戶端下發(fā)一個(gè)指令,讓客戶端上傳特定時(shí)間段內(nèi)的流量統(tǒng)計(jì)數(shù)據(jù),然后結(jié)合用戶使用時(shí)長判斷 App 的流量消耗是否真的存在異常。
NetworkStatsManager 是 API 23 后的流量統(tǒng)計(jì)管理器,它可以獲取某個(gè)時(shí)段的流量信息,也可以獲取不同網(wǎng)絡(luò)類型下的流量消耗,它最大的不足就是用戶體驗(yàn)比較差,需要用戶開啟“查看使用情況”權(quán)限。
下面是一些使用網(wǎng)絡(luò)或?qū)α髁康南谋容^多的場景。
API 請求
-
升級包
各種升級的資源包,比如 App 升級包、WebView 使用的 H5 Zip 包、RN 使用到的 bundle 包等;
-
配置
在 App 做大后,還要用到各種配置信息,比如做 A/B 測試時(shí)使用的配置信息、運(yùn)營活動(dòng)下發(fā)的配置信息;
-
圖片
圖片是流量消耗的大戶,圖片的下載和上傳都非常消耗流量;
-
監(jiān)控
在 App 做大后,還會(huì)做各種監(jiān)控功能,比如 APM 監(jiān)控的各種數(shù)據(jù)都需要網(wǎng)絡(luò)才能上傳到服務(wù)端;
3.2.1 流量優(yōu)化的三個(gè)要點(diǎn)
在講怎么用 NetworkStatsManager 獲取流量消耗前,我們要先了解下流量優(yōu)化的三個(gè)要點(diǎn)。
1. 不能只看絕對值
絕對值不能作為流量消耗偏高的唯一統(tǒng)計(jì)標(biāo)準(zhǔn),不能說 App 消耗了 10M 的流量,那就要馬上去優(yōu)化。
絕對值的對比是沒有意義的,比如用了 App 30 分鐘,瀏覽了很多的商品或視頻,那用了 10M 可能已經(jīng)算少了。
2. 對比競品
我們最好是對比競品在相同的場景下的流量消耗,比如和競品一起跑一個(gè)發(fā)布評論的主流程,這里要注意,要保證兩個(gè) App 發(fā)布的評論是相同的,而且圖片也是相同的,以保證變量是唯一的。
這樣對比一下,如果我們和競品的流量消耗差距比較大,那我們對流量優(yōu)化的步伐,就應(yīng)該加快一些,絕對值跟對比對比可以結(jié)合使用。
3. 設(shè)定預(yù)期
我們要判斷一下新上功能的流量消耗,比如我們要預(yù)期是用戶使用新功能后,單次消耗流量應(yīng)該為 300K 左右,但是上線后超過了 300K 時(shí),我們就要確認(rèn)下流量消耗是否偏高。
3.2.2 NetworkStatsManager 基本用法
1. readNetworkStats
2. querySummary
下面是常用的 querySummary 方法各個(gè)參數(shù)的介紹。
querySummary 的內(nèi)部處理流程如下。
在這個(gè)流程中,最重要的就是在 NetworkStatsCollection 的 mStats 中讀取系統(tǒng)已經(jīng)保存好的網(wǎng)絡(luò)統(tǒng)計(jì)數(shù)據(jù)。
3. 申請授權(quán)
使用 NetworkStatsManager 需要額外的權(quán)限 PACKAGE_USAGE_STATS , 這個(gè)權(quán)限是系統(tǒng)權(quán)限,需要主動(dòng)引導(dǎo)用戶開啟應(yīng)用的“有權(quán)查看使用情況的應(yīng)用”權(quán)限。
首先在清單文件中權(quán)限聲明。
然后在代碼中主動(dòng)引導(dǎo)用戶開啟權(quán)限。
3.3 TrafficStats
TrafficStats 是 API 8 后提供的一種流量數(shù)據(jù)統(tǒng)計(jì)方案,它統(tǒng)計(jì)到的數(shù)據(jù)其實(shí)是我們手機(jī)上次重啟后的流量消耗,也就是重啟前的流量是統(tǒng)計(jì)不到的。
TrafficStats 常用方法:
-
getUidRxBytes(int uid)
獲取指定 Uid 的接收流量;
-
getTotalTxBytes()
獲取 App 總發(fā)送流量;
下面我們是 TrafficStats 的使用方式。
一般情況下不使用 TrafficStats,因?yàn)樗嬖谙旅鎯蓚€(gè)問題:
-
無法獲取特定應(yīng)用的流量消耗
無法獲取特定應(yīng)用的流量消耗,只能給我們一個(gè)總的流量消耗值,這個(gè)值對于我們解決問題來說幫助不大。
-
無法獲取特定時(shí)間段的流量消耗
比如線上某個(gè)用戶反饋發(fā)現(xiàn)昨天 App 的流量消耗過多,這時(shí)我們是無法知道用戶具體消耗了多少流量的。
4. 三個(gè)流量優(yōu)化方案
下面我們來看下三個(gè)流量優(yōu)化方案:
- 數(shù)據(jù)緩存
- 數(shù)據(jù)壓縮
- 圖片壓縮
4.1 數(shù)據(jù)緩存
下面我們來看下數(shù)據(jù)緩存的兩個(gè)方法:
- OkHttp 緩存
- 過期時(shí)間與增量更新
4.1.1 OkHttp 緩存
如果我們仔細(xì)跟一下自己項(xiàng)目中的接口,就會(huì)發(fā)現(xiàn)很多對實(shí)時(shí)性沒有那么高要求的接口,使用緩存不僅可以節(jié)約流量,而且能大幅提升數(shù)據(jù)訪問速度。
我們常用的網(wǎng)絡(luò)庫,比如 OkHttp 和 Volley,都有比較好的緩存實(shí)踐。
而且沒做緩存對用戶體驗(yàn)也不好,一般的 App 會(huì)在打開后顯示一個(gè)無數(shù)據(jù)的界面,和展示上一次的數(shù)據(jù)相比,這個(gè)用戶體驗(yàn)其實(shí)是比較差的。
1. 無網(wǎng)攔截器
下面我們重點(diǎn)看下 OkHttp 的緩存實(shí)踐,首先定義一個(gè)無網(wǎng)攔截器。
然后是給 OkHttpClient 添加攔截器。
添加了無網(wǎng)絡(luò)攔截器后,當(dāng)無網(wǎng)絡(luò)的情況下打開我們的 App 時(shí),也能獲取到上一次的數(shù)據(jù),也能使用 App,這樣就能提升用戶體驗(yàn)。
2. OkHttp 緩存處理流程
OkHttp 的緩存攔截器對于請求的處理流程如下。
4.1.2 過期時(shí)間與增量更新
1. 過期時(shí)間
在服務(wù)端返回的數(shù)據(jù)中加上一個(gè)過期時(shí)間,這樣我們每次請求的時(shí)候判斷一下有沒有過期,如果沒有過期就不需要去重新請求。
2. 增量更新
數(shù)據(jù)增量更新的具體思路,就是在數(shù)據(jù)中加上一個(gè)版本的概念,每次接收數(shù)據(jù)都進(jìn)行版本對比,只接收有變化的數(shù)據(jù)。
這樣傳輸?shù)臄?shù)據(jù)量就會(huì)減少很多,比如省市區(qū)和配置等數(shù)據(jù)比較少更新,如果每次都要請求省市區(qū)的數(shù)據(jù),這就是在浪費(fèi)流量。
我們只需要更新發(fā)生變化的數(shù)據(jù),因?yàn)楹头?wù)器相關(guān)比較密切,在這里就不給大家舉例了。
4.2 數(shù)據(jù)壓縮
1. Gzip
對于 Post 請求,Body 是用 Gzip 壓縮的,也就是請求的時(shí)候帶上 Gzip 請求頭,服務(wù)端返回的時(shí)候也加上 Gzip 壓縮,這樣數(shù)據(jù)流就是被壓縮過的。
2. 壓縮請求頭
請求頭也占用一定的體積,在請求頭不變的情況下,我們可以只傳遞一次,以后都只需要傳遞上一次請求頭的 MD5 值,服務(wù)端做一個(gè)緩存,在需要請求頭中的某些信息時(shí),就可以直接從之前的緩存中取。
3. 合并網(wǎng)絡(luò)請求
每一個(gè)網(wǎng)絡(luò)請求都會(huì)有冗余信息,比如請求頭,而合并網(wǎng)絡(luò)請求就可以減少冗余信息的傳遞;
4.3 圖片壓縮
1. 縮略圖
圖片壓縮的第一個(gè)手段,就是在列表中優(yōu)先使用縮略圖,因?yàn)檎故驹瓐D會(huì)加大內(nèi)存消耗和流量消耗,而且在列表中直接展示原圖沒有意義。
下面是原圖和縮略圖的對比大小,縮略圖尺寸為原圖的 50%,大小為原圖的 10%。
2. WebP
圖片壓縮的第二個(gè)手段,就是使用 Webp 格式,下面是同一張圖片在 PNG 格式和 WebP 格式下的對比,WebP 格式的大小為 PNG 格式的 51%。
3. Luban
比如我們在上傳圖片的時(shí)候,做一個(gè)壓縮比如在本地是一個(gè) 2M 的圖片,完整地上傳上去意義不大,只會(huì)增加我們的流量消耗,最好是壓縮后再上傳。
而在圖片壓縮上做得比較好的就是魯班,下面我們來看下魯班的使用方法。
首先添加依賴。
dependencies {
// 圖片壓縮
implementation 'top.zibin:Luban:1.1.8'
}
然后添加對圖片進(jìn)行壓縮。
下面這張圖片的原始大小為 1.6M,壓縮后變成了 213KB,體積為原始大小的 13%。
5. 三個(gè)質(zhì)量優(yōu)化方案
在前面我們學(xué)習(xí)了網(wǎng)絡(luò)請求流量優(yōu)化,但是實(shí)際上,對用戶體驗(yàn)破壞最大的是網(wǎng)絡(luò)請求質(zhì)量差,我們一般在開發(fā)或測試階段,都是在公司用 WiFi 測試,網(wǎng)絡(luò)質(zhì)量比較好,假設(shè)我們的 App 在流量消耗上問題不大,但是用戶經(jīng)常反饋界面打不開、打開慢、圖片加載不出來等問題。
這時(shí)用戶很有可能會(huì)卸載我們的 App ,轉(zhuǎn)向競品,只有在網(wǎng)絡(luò)請求質(zhì)量高,用戶體驗(yàn)好,繼續(xù)使用我們的 App 時(shí),用戶才有可能遇到流量消耗的問題,所以網(wǎng)絡(luò)質(zhì)量優(yōu)化比流量優(yōu)化更關(guān)鍵。
網(wǎng)絡(luò)質(zhì)量優(yōu)化的兩個(gè)指標(biāo):
- 網(wǎng)絡(luò)請求成功率
- 網(wǎng)絡(luò)請求速度
這兩個(gè)指標(biāo)都會(huì)影響用戶體驗(yàn),在介紹網(wǎng)絡(luò)請求質(zhì)量優(yōu)化前,我們先來看下一個(gè) Http 請求的過程。
客戶端發(fā)出一個(gè)請求
,這個(gè)請求到達(dá)運(yùn)營商的 DNS 服務(wù)器,然后被解析成對應(yīng)的 IP 地址。
創(chuàng)建連接
,會(huì)走 TCP 三次握手,然后根據(jù) IP 地址找對對應(yīng)的服務(wù)器,發(fā)送一個(gè)請求。
服務(wù)器找到對應(yīng)的資源,然后原路返回給客戶端
.
5.1 HttpDNS
首先來看怎么在發(fā)出請求這一步上優(yōu)化,網(wǎng)絡(luò)請求成功率與速度一上來就受 DNS 服務(wù)器的影響,如果我們的 DNS 解析到 IP 地址的過程被劫持或 DNS 解析慢,都會(huì)嚴(yán)重影響用戶體驗(yàn)。
DNS 被劫持的結(jié)果就是用戶得到的數(shù)據(jù)并不是我們真實(shí)想要提供給用戶的數(shù)據(jù),如果 DNS 解析慢,那用戶等待的請求時(shí)間就會(huì)變長。
所以 DNS 優(yōu)化是網(wǎng)絡(luò)質(zhì)量優(yōu)化的第一步,我們使用 HttpDNS,繞過運(yùn)營商域名解析過程,HttpDNS 不是使用傳統(tǒng)的 DNS 協(xié)議,向 DNS 服務(wù)器的 53 端口發(fā)送請求,而是使用 Http 協(xié)議,向服務(wù)器的 80 端口發(fā)送請求。
這樣做的好處有兩個(gè):
-
防劫持
降低 Local DNS 劫持,繞過運(yùn)營商域名解析過程;
-
提升速度
降低平均訪問時(shí)長,因?yàn)楣?jié)省了一次解析過程;
騰訊云和阿里云都提供了 HttpDNS 服務(wù),具體的實(shí)現(xiàn)可能看他們的官方文檔。
5.2 Http 協(xié)議版本優(yōu)化
剛剛說到了網(wǎng)絡(luò)請求的第二步是創(chuàng)建連接,當(dāng)中涉及 TCP 三次握手,這個(gè)過程是比較長的,如果每次請求都要走三次握手,那這個(gè)效率是比較低的。
所以 Http 的不同版本對這點(diǎn)的優(yōu)化也非常多,下面是 Http 協(xié)議不同版本之間的主要區(qū)別。
-
Http 1.0
較老的版本,現(xiàn)在已經(jīng)很少見到用這個(gè)版本的服務(wù)了,它最大的缺點(diǎn)就是 TCP 連接不復(fù)用,每個(gè) TCP 連接只能發(fā)送 1 個(gè)請求,如果要請求別的資源,就必須重新建立一個(gè)連接。
TCP 創(chuàng)建連接的成本非常高,需要三次握手,并且在開始階段發(fā)送速度比較慢,也就是 Http 1.0 的性能非常差。
-
Http 1.1
Http 1.1 的出現(xiàn)只比 1.0 晚了半年,它最大的變化就是引入了持久連接,從這個(gè)版本開始,是默認(rèn)不關(guān)閉的,可以被多個(gè)網(wǎng)絡(luò)請求復(fù)用,這樣效率就有了很大的提升。
它還是有些缺陷,就是它雖然允許 TCP 復(fù)用,但是同一個(gè) TCP 連接里面的所有數(shù)據(jù)通訊必須按順序來,也就是處理完一個(gè)請求后,再響應(yīng)下一個(gè)請求。
如果前面的網(wǎng)絡(luò)請求比較慢,那后面的請求也只能等著。
-
Http 2.0
Http 2.0 是一個(gè)二進(jìn)制協(xié)議,它最大的改進(jìn)就是客戶端和服務(wù)端可以同時(shí)發(fā)送多個(gè)請求和響應(yīng),不需要像 Http 1.1 一樣按順序請求,是一個(gè)雙向的實(shí)時(shí)通信。
這點(diǎn)是 Http 2.0 相對于 Http 1.1 的最大的改進(jìn),大家以后要跟服務(wù)端配合時(shí),有得選的前提下,盡可能選擇高版本的 Http 協(xié)議。
5.3 資本優(yōu)化
最后一個(gè)優(yōu)化方案就是砸錢,常見的手段有 CDN 加速、提高帶寬、動(dòng)靜資源分離。
大家需要注意,使用 CDN 后,如果某個(gè)資源需要更新,更新完成后是需要清理緩存的,這些優(yōu)化不涉及客戶端,同時(shí)也不要忘了減少傳輸量,注意請求的時(shí)機(jī)和頻率,這一條和我們前面講到的流量優(yōu)化相關(guān)。
參考資料
- 慕課網(wǎng)—Top團(tuán)隊(duì)大牛帶你玩轉(zhuǎn)Android性能分析與優(yōu)化
- macOS Charles 4.x版本的安裝及使用
- Setting up Charles to Proxy your Android Device
- 網(wǎng)絡(luò)安全配置
- Charles網(wǎng)絡(luò)設(shè)置
- Stetho
- Android應(yīng)用流量統(tǒng)計(jì)——NetworkStatsManager使用
- AppOpsManager權(quán)限檢測適配
- Android中的uid
- Glide 4.X 使用自定義okhttp 加載圖片(忽略https驗(yàn)證)
- ProcessLifecycleOwner判斷Android應(yīng)用程序前后臺切換
- NetworkStatsCollection