書(shū)接上文,Android 性能優(yōu)化(一):閃退、卡頓、耗電、APK
從用戶體驗(yàn)角度有四個(gè)性能優(yōu)化方向:
- 追求穩(wěn)定,防止崩潰
- 追求流暢,防止卡頓
- 追求續(xù)航,防止耗損
- 追求精簡(jiǎn),防止臃腫
防止卡頓
卡頓的場(chǎng)景通常與用戶交互體驗(yàn)最直接,分別為UI、啟動(dòng)、跳轉(zhuǎn)、響應(yīng)四個(gè)方面,如下圖所示。
?主要有兩大因素:
- 界面繪制:層級(jí)深、元素復(fù)雜、頻繁刷新等。由于這些原因?qū)е驴D的場(chǎng)景更多出現(xiàn)在啟動(dòng)后的初始界面以及跳轉(zhuǎn)到頁(yè)面的繪制上。
- 數(shù)據(jù)處理:數(shù)據(jù)處理量太大。一般分為三種情況,一是數(shù)據(jù)在處理 UI 線程,二是數(shù)據(jù)處理占用 CPU 高,導(dǎo)致主線程拿不到時(shí)間片,三是內(nèi)存增加導(dǎo)致 GC 頻繁,從而引起卡頓。
UI優(yōu)化
主要是繪制和刷新的卡頓,典型表現(xiàn)在頁(yè)面的列表的滑動(dòng)加載和刷新。
針對(duì)繪制可以使用懶加載,針對(duì)刷新可以使用局部刷新。詳情參考:
(四)RecycleView 滑動(dòng)到置頂、Adapter局部刷新
(七)RecycleView 性能提升、卡頓優(yōu)化
啟動(dòng)優(yōu)化
啟動(dòng)速度是用戶第一印象,過(guò)長(zhǎng)會(huì)讓用戶失去耐心; 根據(jù)行業(yè)數(shù)據(jù)統(tǒng)計(jì),啟動(dòng)速度與用戶留存有著直接相關(guān);
對(duì)于開(kāi)發(fā)者來(lái)說(shuō),通過(guò)各種技術(shù)手段來(lái)提升啟動(dòng)性能縮減啟動(dòng)時(shí)長(zhǎng),對(duì)整站業(yè)務(wù)的各項(xiàng)指標(biāo)提升都會(huì)有較大幫助。因此,啟動(dòng)優(yōu)化是各個(gè)客戶端團(tuán)隊(duì)在體驗(yàn)優(yōu)化方向上十分重要的一環(huán)。
評(píng)估量化
在計(jì)劃優(yōu)化立項(xiàng)之前,先確定兩件事。
一、目標(biāo)任務(wù)的啟動(dòng)是否值得優(yōu)化?
衡量一個(gè)任務(wù)是否要啟動(dòng)有兩個(gè)關(guān)鍵指標(biāo):“投入產(chǎn)出比”、“產(chǎn)出風(fēng)險(xiǎn)比”。
- 投入產(chǎn)出比:當(dāng)完成優(yōu)化任務(wù)需要 3人/天,收獲90分位 100ms 的優(yōu)化產(chǎn)出;另一個(gè)只收獲90分位 10ms 的優(yōu)化產(chǎn)出。顯而易見(jiàn)選前者。因?yàn)閮?yōu)化的前期追求的的是優(yōu)化收益,等到后續(xù)開(kāi)啟二期任務(wù)、三期任務(wù)需要做到更加極致時(shí),才會(huì)考慮成本高但收益低的任務(wù)。
- 產(chǎn)出風(fēng)險(xiǎn)比:當(dāng)做優(yōu)化任務(wù)時(shí),可能會(huì)有風(fēng)險(xiǎn)點(diǎn)影響某個(gè)業(yè)務(wù),這時(shí)需要衡量風(fēng)險(xiǎn)影響范圍的大小,然后和業(yè)務(wù)方商討劣化和優(yōu)化的抉擇,最后敲定任務(wù)排期。
二、優(yōu)化的預(yù)期結(jié)果是否可量化?
有個(gè)比較簡(jiǎn)單的方法就是對(duì)標(biāo),比如,競(jìng)品App或主流App。
當(dāng)然,啟動(dòng)優(yōu)化最終目標(biāo)是“秒開(kāi)”,即需要 1s 內(nèi)完成用戶的啟動(dòng)流程。
量化建設(shè)
工作的價(jià)值體現(xiàn)就在于建設(shè)豐富的數(shù)據(jù)大盤(pán)來(lái)明確以什么形式、從什么角度、監(jiān)控什么指標(biāo)。
啟動(dòng)優(yōu)化的整體監(jiān)控可以為:
- 秒開(kāi)率、2秒開(kāi)率、3秒開(kāi)率...
- 90分位總體性能、90分位各階段性能;(波動(dòng)較小,不會(huì)被極端的case影響曲線)
- 分版本各階段各項(xiàng)指標(biāo)、整體各階段各項(xiàng)指標(biāo)、主版本各階段各項(xiàng)指標(biāo)
- 分場(chǎng)景,如有無(wú)廣告等等
- ...
耗時(shí)定義
業(yè)內(nèi)常見(jiàn)的app啟動(dòng)過(guò)程階段一般分為「啟動(dòng)階段」和「首刷階段」。
-
啟動(dòng)階段:指用戶點(diǎn)擊icon到見(jiàn)到app的首頁(yè),起點(diǎn)為
Application
的onCreate,終點(diǎn)
Activity
的onWindowFocusChanged
()。
-
首刷階段:指用戶見(jiàn)到app的首頁(yè)到首頁(yè)列表內(nèi)容展現(xiàn)起點(diǎn)為
Activity
的onCreate,
終點(diǎn)列表的onAttachedToWindow() 。
為了確保啟動(dòng)優(yōu)化量化指標(biāo)的數(shù)據(jù)能穩(wěn)定和完整。因此,排除其他啟動(dòng)場(chǎng)景的統(tǒng)計(jì):
- 啟動(dòng)過(guò)程中App退后臺(tái)
- 用戶未登錄場(chǎng)景
- 特殊場(chǎng)景下的開(kāi)屏廣告,比如有復(fù)雜的聯(lián)動(dòng)動(dòng)效
-
站外push、deeplink
拉起。
檢測(cè)工具
Android Studio 自帶的Profiler工具。
如果是 Android 10 及以上就用 Perfetto。
啟動(dòng)階段
在App的啟動(dòng)流程中,有非常多的啟動(dòng)任務(wù)全部在Application的onCreate里被執(zhí)行,有主線程的有非主線程的,但是不可避免的是,二者都會(huì)對(duì)啟動(dòng)的性能帶來(lái)?yè)p耗。所以我們需要做的第一件重要的事情就是“啟動(dòng)任務(wù)重排與刪減”。
可以把啟動(dòng)任務(wù)按優(yōu)先級(jí)分類(lèi):
- 剛需高優(yōu)任務(wù):這類(lèi)任務(wù)初始化,根本無(wú)法進(jìn)入后續(xù)流程。所以,不可延遲,必須第一時(shí)間最高優(yōu)先級(jí)執(zhí)行。比如網(wǎng)絡(luò)庫(kù)、路由庫(kù)、存儲(chǔ)庫(kù)等基礎(chǔ)庫(kù)的初始化。
- 非剛需高優(yōu)任務(wù):這類(lèi)任務(wù)不初始化,不影響后續(xù)首頁(yè)UI刷新,比如拉取ab實(shí)驗(yàn)配置、ip直連、push推送通知服務(wù)、長(zhǎng)鏈接等基礎(chǔ)建設(shè)項(xiàng)。
- 非剛需低優(yōu)任務(wù):這類(lèi)任務(wù)對(duì)啟動(dòng)階段后續(xù)流程無(wú)決定性影響,可以放在啟動(dòng)階段結(jié)束之后再執(zhí)行或懶加載。比如 x5內(nèi)核、在線客服sdk、在線反饋SDK、登陸相關(guān)極驗(yàn)/深知SDK、LBS定位、搜索記錄、城市json數(shù)據(jù)解析、Flutter/RN/Weex引擎初始化等。
可以考慮封裝一個(gè)任務(wù)管理器,大致代碼參考如下:
TaskManager.getInstance().beginWith(A)
.then(B)
.then(C, D)
.then(E)
.enqueue();
TaskManager.getInstance().runAfterStartup({ xxx; })
通過(guò)任務(wù)的大致非精細(xì)化的排布,我們不僅僅可以對(duì)啟動(dòng)任務(wù)能夠很好的進(jìn)行監(jiān)控,還可以更加容易的找出不合理項(xiàng)。
首刷階段
從業(yè)務(wù)的角度出發(fā),找到對(duì)啟動(dòng)流程影響最大的業(yè)務(wù)為切入點(diǎn),嘗試尋求更優(yōu)的解法。
根據(jù)業(yè)務(wù)流程進(jìn)行如下考慮:
- IO 請(qǐng)求優(yōu)先級(jí)提高,例如開(kāi)屏廣告接口請(qǐng)求提前發(fā)起。
- IO wait期間,優(yōu)化各項(xiàng)初始化,減少大量數(shù)據(jù)處理導(dǎo)致主線程拿不到時(shí)間片CPUTime。
- 首頁(yè)UI復(fù)雜度簡(jiǎn)化,列表的第一屏數(shù)據(jù)盡量走緩存。
1、 SplashActivity與MainActivity合并
當(dāng)App啟動(dòng)時(shí),會(huì)先進(jìn)入SplashActivity等廣告接口返回展示廣告,然后轉(zhuǎn)進(jìn)MainActivity。這個(gè)流程對(duì)性影響最大的點(diǎn)是:等廣告接口時(shí),無(wú)法對(duì)后續(xù)業(yè)務(wù)做預(yù)加載,業(yè)務(wù)都在MainActivity。另外,啟動(dòng)階段需要連續(xù)啟動(dòng)兩個(gè)Activity,至少帶來(lái) 百毫秒 級(jí)別的劣化。
合并前后,流程圖如下:?
廣告原先是以Activity的形式在展現(xiàn),現(xiàn)在被抽離成 View 的形式去承載邏輯,在 onCreate 中,廣告View添加進(jìn) DecorView中,完成對(duì)首頁(yè)布局的遮罩。在廣告View展示logo圖時(shí),底下首頁(yè)框架是可以進(jìn)行預(yù)加載和渲染。?
補(bǔ)充注意事項(xiàng):?
MainActivity 作為launchAtivity的單實(shí)例問(wèn)題。App從后臺(tái)進(jìn)前臺(tái),其他二級(jí)頁(yè)面跳轉(zhuǎn)到首頁(yè)的依然走生命周期的現(xiàn)象。MainAcitvity 的 launchmode 需要設(shè)置為 singleTop。?
首頁(yè)彈窗管理器需要延遲初始化。在廣告View覆蓋在上面時(shí),先暫停彈層管理器的生命周期,避免出現(xiàn)其他內(nèi)容蓋住廣告。
2、多Tab延遲加載+懶加載
一般MainActivity中的Tab會(huì)以fragment形式被提起預(yù)加載。而首刷階段的終點(diǎn)是在首頁(yè)列表View的onAttachedToWindow()生命期中。
因此,可以考慮其他Tab進(jìn)行延遲加載和懶加載。即用戶不迫切切換Tab,就在首頁(yè)View繪制時(shí)進(jìn)行預(yù)加載;否則就在用戶點(diǎn)擊Tab切換事件中進(jìn)行加載(可能卡頓劣化,需風(fēng)險(xiǎn)評(píng)估)。
3、布局優(yōu)化
比如,廣告View可以在接口返回?cái)?shù)據(jù)且經(jīng)過(guò)素材校驗(yàn)后,再進(jìn)行布局的加載。類(lèi)似的Banana位、搜索框、其他圖片廣告等。
- 布局復(fù)用,使用<include>標(biāo)簽重用layout;
- 提高顯示速度,使用<ViewStub>延遲View加載;
- 減少層級(jí),使用<merge>標(biāo)簽替換父級(jí)布局;
-
其他:使用wrap_content會(huì)增加measure計(jì)算成本;kotlin by lazy,動(dòng)態(tài)內(nèi)容加載的場(chǎng)景等。
4、繪制優(yōu)化
過(guò)度繪制是指在屏幕上的某個(gè)像素在同一幀的時(shí)間內(nèi)被繪制了多次。
在多層次重疊的 UI 結(jié)構(gòu)中,如果不可見(jiàn)的 UI 也在做繪制的操作,就會(huì)導(dǎo)致某些像素區(qū)域被繪制了多次,從而浪費(fèi)了多余的 CPU 以及 GPU 資源。
- 移除 XML 中非必須的背景,移除 Window 默認(rèn)的背景、按需顯示占位背景圖片;
- 使用 canvas.clipRect() 識(shí)別可見(jiàn)區(qū)域,只在這個(gè)區(qū)域內(nèi)才會(huì)被繪制。
可以通過(guò)LayoutInspector和Android的調(diào)試工具去各自分析了,如果打開(kāi)LayoutInspector肉眼可見(jiàn)都是紅色,趕緊改。
5、page cache
給首頁(yè)的數(shù)據(jù)流增加頁(yè)面級(jí)別緩存,讓首頁(yè)首刷不依賴接口返回就可以展示。
以上啟動(dòng)優(yōu)化的點(diǎn)可以更詳細(xì)的、更極致的優(yōu)化,需要根據(jù)各自APP情況而定。
階段成果
優(yōu)化結(jié)果:
- 從2.87 降至最低時(shí)2.1,降幅26.8%
- 再進(jìn)行優(yōu)化后效果不明顯,遇到瓶頸?
優(yōu)化瓶頸
瓶頸是什么?——APP加殼
- APP加固中的加殼,嚴(yán)重拖慢了APP啟動(dòng)性能;
- 且愛(ài)加密對(duì)加殼后的性能損耗無(wú)法優(yōu)化;
如何突破瓶頸?——混淆+VMP
- 殼的作用:對(duì)APPDex文件進(jìn)行加密,防止APP被反編譯,導(dǎo)致源代碼泄露;
- 替代方案: 對(duì)代碼進(jìn)行混淆,這也是行業(yè)通用做法 VMP加密,一種虛擬機(jī)加密技術(shù),加密后將原有代碼片段替換成新的片段,在執(zhí)行時(shí)再解密;
- 去殼需得到安全部門(mén)認(rèn)可: 通過(guò)三方機(jī)構(gòu)對(duì)加殼、僅混淆、混淆+VMP加密包進(jìn)行安全測(cè)試; 得出結(jié)論為安全性 混淆+VMP > 僅混淆 > 僅加殼??
防劣化同樣重要
在線上優(yōu)化工作開(kāi)展完成且取得了相應(yīng)成果后,絕對(duì)不能放松警惕。
優(yōu)化一次獲得的效果并不是最重要的,最重要的是要有持續(xù)的、穩(wěn)定的優(yōu)化效果。
對(duì)于線上用戶來(lái)說(shuō),其實(shí)可能并不關(guān)心這個(gè)版本或者這幾個(gè)版本是不是變快了,大家可能更需要的是長(zhǎng)時(shí)間的良好體驗(yàn)。
對(duì)于我們這些開(kāi)發(fā)者來(lái)說(shuō),長(zhǎng)時(shí)間的良好體驗(yàn)可能才能改變大家對(duì)自家 App 的性能印象,讓大家認(rèn)可自家 App 的啟動(dòng)性能,這也是我們優(yōu)化的初衷。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-854661.html
因此,防劣化和優(yōu)化同樣重要。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-854661.html
到了這里,關(guān)于Android 性能優(yōu)化(六):?jiǎn)?dòng)優(yōu)化的詳細(xì)流程的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!