美國(guó)疫情最新消息今天又封了石家莊百度搜索引擎優(yōu)化
做客戶端開發(fā)都基本都做過性能優(yōu)化,比如提升自己所負(fù)責(zé)的業(yè)務(wù)的速度或流暢性,優(yōu)化內(nèi)存占用等等。但是大部分開發(fā)者所做的性能優(yōu)化可能都是針對(duì)中小型 APP 的,大型 APP 的性能優(yōu)化經(jīng)驗(yàn)并不會(huì)太多,畢竟大型 APP 就只有那么幾個(gè),什么是大型 APP 呢?以飛書來(lái)說(shuō),他的業(yè)務(wù)有 im,郵箱,日歷,小程序,文檔,視頻會(huì)議……等等,包體積就有大幾百 M,像這種業(yè)務(wù)非常多且復(fù)雜的 APP 都可以認(rèn)為是大型 APP。所以我在這篇文章主要講一下大型 APP 是怎么做性能優(yōu)化的,給大家在性能優(yōu)化一塊提供一個(gè)新的視角和啟發(fā)。在這篇文章中,我主要會(huì)講一下這兩個(gè)主題:
-
大型 app 相比于中小型 app 在做性能優(yōu)化時(shí)的異同點(diǎn)
-
大型 app 性能優(yōu)化的思路
大型和小型應(yīng)用性能優(yōu)化的異同點(diǎn)
1.1 相同點(diǎn)
性能優(yōu)化在本質(zhì)上以及在優(yōu)化維度上都是一樣的。性能優(yōu)化的本質(zhì)是合理且充分的使用硬件資源,讓程序表現(xiàn)的更好;并且都需要基于應(yīng)用層、系統(tǒng)層、硬件層三個(gè)維度來(lái)進(jìn)行優(yōu)化
1.2 不同點(diǎn)
針對(duì)系統(tǒng)層和硬件層的優(yōu)化都是一樣,有區(qū)別的主要是針對(duì)應(yīng)用層的優(yōu)化。
中小型 app 做業(yè)務(wù)和做性能優(yōu)化的往往是同一個(gè)人,在做優(yōu)化的時(shí)候,只需要考慮單個(gè)業(yè)務(wù)最優(yōu)即可,我們只需要給這些業(yè)務(wù)充足的硬件資源(比如給更多的內(nèi)存資源:緩存更多的數(shù)據(jù),給更多的 cpu 資源:用更多的線程來(lái)執(zhí)行業(yè)務(wù)邏輯,給更多的磁盤資源:緩存足夠的本地?cái)?shù)據(jù)),并且合理的使用就能讓業(yè)務(wù)表現(xiàn)的更好。只要這些單個(gè)的業(yè)務(wù)性能表現(xiàn)好,那么這款 app 的整體性能品質(zhì)是不錯(cuò)
和中小型 APP 不同的是,大型 APP 業(yè)務(wù)多且復(fù)雜,各個(gè)業(yè)務(wù)的團(tuán)隊(duì)很可能都不在一個(gè)部門,不在同一個(gè)城市。在這種情況下,如果每個(gè)業(yè)務(wù)也去追求自己業(yè)務(wù)的性能最優(yōu),同樣是在自己的業(yè)務(wù)中使用更多的線程,使用更多的緩存,使用更多 cpu 的方式來(lái)使自己業(yè)務(wù)表現(xiàn)更好,那么就會(huì)導(dǎo)致 APP 整體的性能急劇劣化。因此大型 APP 需要有一個(gè)專門團(tuán)隊(duì)來(lái)做性能優(yōu)化的,這個(gè)團(tuán)隊(duì)需要脫離某一個(gè)具體業(yè)務(wù),站在全局的視角來(lái)讓 APP 整體表現(xiàn)更優(yōu)。
大型應(yīng)用性能優(yōu)化方案
總的來(lái)說(shuō)由于資源是有限的,在中小型 APP 上,業(yè)務(wù)少,資源往往是充足的,我們做性能優(yōu)化時(shí)往往考慮的是怎么將資源充分的發(fā)揮出來(lái),而在大型 APP 上,資源往往是不足的,做性能優(yōu)化時(shí)需要考慮即能充分發(fā)揮硬件資源,又需要進(jìn)行合理的分配,當(dāng)我們站在全局的視角來(lái)進(jìn)行資源分配時(shí),就需要考慮到這三個(gè)點(diǎn):
- 如何管控業(yè)務(wù)對(duì)資源的使用
- 如何度量業(yè)務(wù)對(duì)資源的消耗
- 如何讓業(yè)務(wù)在資源緊張時(shí)做出更優(yōu)的策略
下面我會(huì)針對(duì)速度及流暢性優(yōu)化、內(nèi)存優(yōu)化這兩個(gè)方向,講一講針對(duì)這這三點(diǎn)的體現(xiàn)。
2.1 速度和流暢性優(yōu)化:如何管控業(yè)務(wù)對(duì)資源的使用
在速度和流暢性方向,中小型 APP 只需要分析和優(yōu)化主路徑的耗時(shí)邏輯;將同步任務(wù)盡量?jī)?yōu)化成異步任務(wù);多進(jìn)行預(yù)加載等方案,即能起很好的優(yōu)化效果。但是對(duì)于大型 APP 來(lái)說(shuō),異步任務(wù)往往非常多,cpu 往往都是打滿的情況,這種情況下主路徑得不到足夠的 cpu 資源,導(dǎo)致速度變慢。所以大型 app 中一般都會(huì)對(duì)業(yè)務(wù)的異步任務(wù),如啟動(dòng)階段的預(yù)加載進(jìn)行管控,因此需要預(yù)加載框架或者類似的框架,來(lái)收斂、管控、以及調(diào)度所有業(yè)務(wù)的預(yù)加載任務(wù)。我們來(lái)看一下在大型 APP 中,通用的預(yù)加載框架是怎么做的。
2.1.1 預(yù)加載框架
想要管控業(yè)務(wù)的預(yù)加載任務(wù),我們需要考慮這兩個(gè)點(diǎn):
首先要將業(yè)務(wù)的預(yù)加載任務(wù)和業(yè)務(wù)進(jìn)行解耦,要能做到即使該預(yù)加載任務(wù)不執(zhí)行,也不會(huì)影響到業(yè)務(wù)的正常使用,并且將預(yù)加載任務(wù)封裝成粒度最小的 task,然后直接將這些 task 丟到到預(yù)加載框架中,我們可以通過單例提供一個(gè) addPreloadTask 方法,業(yè)務(wù)方只需要調(diào)用該接口,并傳入預(yù)加載任務(wù) task 以及一些屬性及配置參數(shù)即可。將預(yù)加載任務(wù)添加到預(yù)加載框架后,業(yè)務(wù)方就不需要進(jìn)行任何其他操作了,是否執(zhí)行、什么時(shí)候執(zhí)行,都交給預(yù)加載框架來(lái)管理。
那么預(yù)加載框架對(duì)于添加進(jìn)來(lái)的 task 如何調(diào)度呢?這就是一個(gè)預(yù)加載框架復(fù)雜的的地方的,我們可以有很多策略,比如這三種:
- 關(guān)鍵節(jié)點(diǎn)調(diào)度策略:比如各個(gè)生命周期階段,頁(yè)面渲染完成階段等去執(zhí)行,也可以在任務(wù)添加進(jìn)來(lái)后立刻執(zhí)行。
- 性能調(diào)度策略:比如判斷 cpu 是否忙碌,溫度是否過高,內(nèi)存是否充足等,只有在性能較好的情況下才進(jìn)行調(diào)度
- 用戶行為調(diào)度策略:如果做的更復(fù)雜一些,還可以結(jié)合用戶的行為指標(biāo),如該業(yè)務(wù)用戶是否會(huì)使用,如果某一個(gè)用戶從來(lái)不適用這個(gè) app 里的這個(gè)功能,那么改業(yè)務(wù)添加進(jìn)來(lái)的預(yù)加載任務(wù)就可以完全舍棄到,這里面可以用一些端智能的方案來(lái)精細(xì)化的控制預(yù)加載任務(wù)的調(diào)度
每種調(diào)度策略不是單獨(dú)執(zhí)行的,我們可以將各種策略整合起來(lái),形成一套完善的調(diào)度策略。
2.2 速度和流暢性優(yōu)化:如何讓業(yè)務(wù)在資源緊張時(shí)做出更優(yōu)的策略
上面提到的是站在全局的視角,如何管控預(yù)加載任務(wù)的,除了預(yù)加載任務(wù),還有很多其他的異步任務(wù)我們都可以用一些框架來(lái)規(guī)范化的管控起來(lái),這里再舉一個(gè)例子,對(duì)于大型 APP 來(lái)說(shuō),業(yè)務(wù)在使用的過程中很容易出現(xiàn)因?yàn)?cpu 或內(nèi)存不足導(dǎo)致卡頓,響應(yīng)慢等性能問題,所以在做性能優(yōu)化時(shí),是需要推動(dòng)業(yè)務(wù)方在資源不足時(shí),做出相應(yīng)策略的,這個(gè)時(shí)候我們就需要降級(jí)框架來(lái)處理了。降級(jí)框架需要解決這兩個(gè)問題:
- 性能指標(biāo)的采集
- 降級(jí)任務(wù)的調(diào)度
2.2.1 降級(jí)框架
想要再資源緊張時(shí)讓業(yè)務(wù)做出優(yōu)化策略,那么對(duì)資源緊張的判斷就是必不可少的一步。我們一般通過在程序運(yùn)行過程中,采集設(shè)備性能指標(biāo)來(lái)判斷資源是否緊張,最基本的性能指標(biāo)有 cpu 使用率,溫度,Java 內(nèi)存,機(jī)型等,除機(jī)型外其他性能指標(biāo)一般都是以固定的頻率進(jìn)行采集,如 cpu 使用率可以 10s 采集一次,溫度可以 30s 采集一次,java 內(nèi)存可以 1 分鐘采集一次,采集的頻率需要考慮對(duì)性能的影響以及指標(biāo)的敏感度,比如 cpu 的使用率采集,需要讀取 proc/stat 的文件并解析,是有一定性能損耗的,所以我們?cè)诓杉瘯r(shí),不能太頻繁;溫度的變化是比較慢的,我們采集的頻率也可以長(zhǎng)一些。降級(jí)框架需要整合這些性能指標(biāo)的采集,減少各個(gè)業(yè)務(wù)自己采集造成不必要的性能損耗。
當(dāng)降級(jí)框架采集到性能指標(biāo),并判斷當(dāng)前資源異常時(shí),通用的做法是通知各個(gè)業(yè)務(wù),業(yè)務(wù)收到通知后再進(jìn)行降級(jí)。比如系統(tǒng)的 lowmemorykiller 機(jī)制,都是采用通知的方式。
但是在大型 APP 中,僅僅將觸發(fā)性能閾值的通知給到各個(gè)業(yè)務(wù)方,效果不會(huì)太好,因?yàn)闃I(yè)務(wù)方可能并不會(huì)去響應(yīng)通知,或者個(gè)別業(yè)務(wù)響應(yīng)了,但是其他業(yè)務(wù)不響應(yīng),依然效果不佳。無(wú)法管控業(yè)務(wù)是否進(jìn)行降級(jí),這顯然不符合在大型 APP 做性能優(yōu)化的思路,那么我們要怎么做呢?
添加任務(wù):我們依然可以推動(dòng)各個(gè)業(yè)務(wù)將降級(jí)的邏輯封裝在 task 中,并且注冊(cè)到降級(jí)框架中,并由降級(jí)框架來(lái)進(jìn)行調(diào)度和管理。因?yàn)橥导?jí)框架注冊(cè) task 時(shí),需要帶上業(yè)務(wù)的名稱,所以我們能也能清楚的知道,那些業(yè)務(wù)有降級(jí)處理邏輯,哪些業(yè)務(wù)沒有,對(duì)于沒有注冊(cè)的業(yè)務(wù),需要專門推動(dòng)進(jìn)行降級(jí)響應(yīng)以及 task 的注冊(cè)。
調(diào)度任務(wù):和預(yù)加載框架一樣,對(duì)于注冊(cè)進(jìn)來(lái)的 task,降級(jí)框架的任務(wù)調(diào)度要考慮清楚調(diào)度的時(shí)機(jī),以 cpu 使用率為例,不同的設(shè)備下的閾值也是不一樣的,高端機(jī)型可能 cpu 的使用率在 70%以上,app 還是流暢的,但是低端機(jī)在 50%以上就開始卡頓了,因此不同的機(jī)型需要根據(jù)經(jīng)驗(yàn)值或者線上數(shù)據(jù)設(shè)置一個(gè)合理的閾值。當(dāng) cpu 到達(dá)這個(gè)閾值時(shí),降級(jí)框架便開始執(zhí)行注冊(cè)到 cpu 列表中的降級(jí)任務(wù),在執(zhí)行降級(jí)任務(wù)時(shí),不需要將隊(duì)列里的 task 全部執(zhí)行,我們可以分批執(zhí)行,如果執(zhí)行到某一批降級(jí) task 時(shí),cpu 恢復(fù)到閾值以下了,后面的降級(jí) task 就可以不用在執(zhí)行了??梢钥吹?#xff0c;通過降級(jí)框架,我們就可以站在全局的維度,去進(jìn)行更好的管控,比如我們可以度量業(yè)務(wù)做降級(jí)任務(wù)的效果,給到一個(gè)評(píng)分,對(duì)于效果不好的,可以推動(dòng)優(yōu)化。
2.3 內(nèi)存優(yōu)化:如何度量業(yè)務(wù)對(duì)資源的消耗
上面兩個(gè)例子將的是在大型 app 中,如何管控業(yè)務(wù)對(duì)資源的使用,以及如何讓業(yè)務(wù)在資源緊張時(shí)做出更優(yōu)的策略的思路,我接著基于內(nèi)存優(yōu)化的方向,講一講如何度量業(yè)務(wù)對(duì)資源的消耗。
當(dāng) app 運(yùn)行過程中,往往只能獲得整體的內(nèi)存的數(shù)據(jù)占用,沒法獲的各個(gè)業(yè)務(wù)消耗了多少內(nèi)存的,因?yàn)楦鱾€(gè)業(yè)務(wù)的數(shù)據(jù)都是放在同一個(gè)堆中的,對(duì)于小型 app 來(lái)說(shuō)這種情況并不是問題,因?yàn)榫湍敲磶讉€(gè)業(yè)務(wù)在使用內(nèi)存,但是對(duì)于大型 app 來(lái)說(shuō)就是一個(gè)問題了,有些業(yè)務(wù)為了自己性能指標(biāo)能更好,會(huì)占用更多的內(nèi)存,導(dǎo)致整體的內(nèi)存占用過高。所以我們需要弄清每個(gè)業(yè)務(wù)到底使用了多少內(nèi)存才能推動(dòng)業(yè)務(wù)進(jìn)行優(yōu)化。
我們可以線下通過分析 hprof 文件或者其他調(diào)試的方式來(lái)弄清楚每個(gè) app 的內(nèi)存占用,但是很多時(shí)候沒有充足的時(shí)間在版本都去統(tǒng)計(jì)一下,或者即使統(tǒng)計(jì)了,也可能因?yàn)槁窂經(jīng)]覆蓋全導(dǎo)致數(shù)據(jù)不準(zhǔn)確。所以我們最好能通過線上監(jiān)控的方式,就能統(tǒng)計(jì)到業(yè)務(wù)的內(nèi)存消耗,并且在內(nèi)存消耗異常的時(shí)候進(jìn)行上報(bào)。
我在這里介紹一種思路。大部分的業(yè)務(wù)都是以 activity 呈現(xiàn)的,所以我們可以監(jiān)聽全局的 activity 創(chuàng)建,在業(yè)務(wù)的 onCreate 最前面統(tǒng)計(jì)一下 java 和 native 內(nèi)存的大小,作為這個(gè)業(yè)務(wù)啟動(dòng)時(shí)的基準(zhǔn)內(nèi)存。然后在 acitvity 運(yùn)行過程中,固定采集在當(dāng)前 activity 下的內(nèi)存并減去 onCreate 時(shí)的基準(zhǔn)內(nèi)存,我們就能度量出當(dāng)前業(yè)務(wù)的一個(gè)內(nèi)存消耗情況了。在該 acitvity 結(jié)束后,我們可以主動(dòng)觸發(fā)一下 gc,然后在和前面的基準(zhǔn)內(nèi)存 diff 一下,也能統(tǒng)計(jì)出該業(yè)務(wù)結(jié)束后的增量?jī)?nèi)存,理想情況下,增量?jī)?nèi)存應(yīng)該是要小于零的,由于 gc 需要 cpu 資源,所以我們只需要開取小部分的采樣率即可。
當(dāng)我們能在運(yùn)行過程中,統(tǒng)計(jì)各個(gè)業(yè)務(wù)的內(nèi)存消耗,那么就可以推動(dòng)內(nèi)存消耗高的業(yè)務(wù)進(jìn)行優(yōu)化,或者當(dāng)某個(gè)版本的某個(gè)業(yè)務(wù)出現(xiàn)較大的劣化時(shí),觸發(fā)報(bào)警等。
除了上面提到的思路,我們也可以統(tǒng)計(jì)在業(yè)務(wù)使用過程中的觸頂次數(shù),計(jì)算出一個(gè)觸頂率的指標(biāo),觸頂及 java 內(nèi)存占用達(dá)到一個(gè)閾值,比如 80%,我們就可以認(rèn)為觸頂了,對(duì)于觸頂次數(shù)高的業(yè)務(wù),同樣也可以進(jìn)行異常上報(bào),然后推動(dòng)業(yè)務(wù)方進(jìn)行修改。這些數(shù)據(jù)和指標(biāo)的統(tǒng)計(jì),都是無(wú)侵入的,所以并不需要我們了解業(yè)務(wù)的細(xì)節(jié)。
如果我們想做的更細(xì)一些,還可以 hook 圖片的創(chuàng)建,hook 集合的 add,remove 等方法,當(dāng)監(jiān)控到大圖片和大集合時(shí),打印堆棧,并將關(guān)鍵信息上報(bào)。在飛書,如果是低端機(jī)中,圖片如果占用內(nèi)存過大的,都會(huì)在 hook 方法中進(jìn)行一些壓縮或者降低質(zhì)量的兜底處理。
總結(jié)
除了速度及流暢性,內(nèi)存方向的優(yōu)化外,還有其他方向的優(yōu)化,如包體積,穩(wěn)定性,功耗等,在大型 APP 上都要基于管控業(yè)務(wù)對(duì)資源的使用;度量業(yè)務(wù)對(duì)資源的消耗;讓業(yè)務(wù)在資源緊張時(shí)做出更優(yōu)的策略這三個(gè)方向去進(jìn)行優(yōu)化,這里我就不再一一展開講了。
當(dāng)然我這里講的優(yōu)化思路并不是大型 app 做性能優(yōu)化的全部,我講的只是在做大型 app 的性能時(shí)相比于中小型 app 需要額外做的,并且也是效果最好的優(yōu)化,這些方案在中小型 app 上可能并不需要。除了我這篇文章講的內(nèi)容外,還有很多優(yōu)化的方案,這些方案不管是在大型 app 還是中小型 app 上都是通用的,比如深入了解業(yè)務(wù),基于業(yè)務(wù)邏輯去做分析和優(yōu)化,抓 trace,分析 trace 等等,或者基于系統(tǒng)層或者硬件層去做一些優(yōu)化等等,這里就不再展開講了。
為了幫助到大家更好的全面清晰的掌握好性能優(yōu)化,準(zhǔn)備了相關(guān)的核心筆記(還該底層邏輯):https://qr18.cn/FVlo89
性能優(yōu)化核心筆記:https://qr18.cn/FVlo89
啟動(dòng)優(yōu)化
內(nèi)存優(yōu)化
UI優(yōu)化
網(wǎng)絡(luò)優(yōu)化
Bitmap優(yōu)化與圖片壓縮優(yōu)化:https://qr18.cn/FVlo89
多線程并發(fā)優(yōu)化與數(shù)據(jù)傳輸效率優(yōu)化
體積包優(yōu)化
《Android 性能監(jiān)控框架》:https://qr18.cn/FVlo89
《Android Framework學(xué)習(xí)手冊(cè)》:https://qr18.cn/AQpN4J
- 開機(jī)Init 進(jìn)程
- 開機(jī)啟動(dòng) Zygote 進(jìn)程
- 開機(jī)啟動(dòng) SystemServer 進(jìn)程
- Binder 驅(qū)動(dòng)
- AMS 的啟動(dòng)過程
- PMS 的啟動(dòng)過程
- Launcher 的啟動(dòng)過程
- Android 四大組件
- Android 系統(tǒng)服務(wù) - Input 事件的分發(fā)過程
- Android 底層渲染 - 屏幕刷新機(jī)制源碼分析
- Android 源碼分析實(shí)戰(zhàn)