本文是《移動App性能評測與優(yōu)化》的讀書筆記。
PS:說是讀書筆記,其實(shí)就是摘錄蛤奢。
移動App的性能測試主要包括:內(nèi)存使用情況、電量消耗陶贼、功能的流暢度等啤贩;
1. 內(nèi)存
1.1 內(nèi)存的主要組成索引:
Native Heap : Native 代碼分配的內(nèi)存,虛擬機(jī)和Android框架本身也會分配拜秧;
Dalvik Heap : Java 代碼分配的對象痹屹;
Dalvik Other : 類的數(shù)據(jù)結(jié)構(gòu)和索引;
so mmap : Native 代碼和常量枉氮;
dex mmap :Java 代碼和常量志衍;
1.2 內(nèi)存測試工具
Android Studio / Memory Monitor : 觀察 Dalvik 內(nèi)存;
dumpsys meminfo : 觀察整體內(nèi)存聊替;
smaps : 觀察整體內(nèi)存的詳細(xì)組成楼肪;
MAT : 詳細(xì)分析 Dalvik內(nèi)存;
1.3 一個(gè)類的內(nèi)存消耗
虛擬機(jī)在創(chuàng)建對象時(shí)的操作:
-
loadClass惹悄,將類信息從 dex 文件中加載進(jìn)內(nèi)存:
讀取 .dexx mmap 中 class 對應(yīng)的數(shù)據(jù)春叫;
分配 native-heap 和 dalvik-heap 內(nèi)存創(chuàng)建 class 對象;
分配 dalvik-LinearAlloc 存放 class 數(shù)據(jù)泣港;
分配 delvik-aux-structure 存放 class 數(shù)據(jù)暂殖;
-
new instance 操作,創(chuàng)建對象實(shí)例
執(zhí)行 .dex mmap 中 <clinit> 和 <init> 的代碼当纱;
分配 delvik-heap 創(chuàng)建 class對象實(shí)例呛每;
dex mmap
在Android應(yīng)用中的作用是映射classes.dex文件
1.4 dex 優(yōu)化
省略掉dex的文件結(jié)構(gòu)(自行查閱)
為了節(jié)省空間,dex將原先在 class 文件中重復(fù)的信息集中放置在一起惫东,并以索引和指針的形式支持快速訪問莉给。
dex 文件中數(shù)據(jù)基本是按類名的字母順序進(jìn)行排列的,這樣同樣包名的類會排在一起廉沮,但程序?qū)嶋H執(zhí)行時(shí)颓遏,同一個(gè)包下的類并不會全部調(diào)用到,而是跨包進(jìn)行交互滞时,但 mmap 加載了整個(gè)頁面叁幢,可能會有很多無用的數(shù)據(jù)。
優(yōu)化;
在APK的編譯流程中坪稽,Proguard 混淆工具正好是能夠?qū)︻惷M(jìn)行修改的曼玩,可以根據(jù)程序運(yùn)行時(shí)的邏輯鳞骤,將那些會互相調(diào)用的類改為為同一個(gè) package 名,這樣就可以使它們的數(shù)據(jù)排布在一起黍判。
1.5 MAT(Memory Analyzer Tool)
使用MAT來分析應(yīng)用的內(nèi)存使用情況豫尽。通常在使用MAT打開hprof
文件后,能夠在首頁看到Top Comnsumers和 component Report等功能顷帖,我們可以快速定位一些大塊的內(nèi)存消耗美旧。但我們在分析時(shí)會發(fā)現(xiàn)系統(tǒng)資源類占據(jù)了很大一部分內(nèi)存,因此為去除這部分對分析的干擾贬墩,我們在使用AndroidSDK
提供的hprof-conv
轉(zhuǎn)換時(shí)需要增加一個(gè)參數(shù):
hporf- conv [-z] <infile><outfile> -z:exclude non-app heaps,such as Zygote
如果hprof文件是已經(jīng)轉(zhuǎn)換過的榴嗅,則可以使用OQL:
//在數(shù)據(jù)中尋找應(yīng)用的Application類對象,將對象地址轉(zhuǎn)換為十進(jìn)制后輸入以下查詢語句:
select * from instanceof java.langObject s where s.@objectAddress> 1107296256
//(后面那串?dāng)?shù)字應(yīng)該是Application類對象的地址)
采用這兩種方法后陶舞,再使用MAT來分析就可以比較容易發(fā)現(xiàn)自身代碼的內(nèi)存問題嗽测。
1.6 測試經(jīng)驗(yàn)
MAT 是探索 Java 堆并發(fā)現(xiàn)問題和好幫手,能夠迅速發(fā)現(xiàn)常見的圖片和大數(shù)組等問題肿孵;
內(nèi)存碎片問題一般隱藏在對象的地址中唠粥;
如需要測試非 Dalvik部分,有必要了解 Linux 的進(jìn)程和內(nèi)存原理颁井、內(nèi)存共享機(jī)制厅贪,熟悉常用命令行工具;
內(nèi)存分配的最小單位是頁面雅宾,通常為4KB养涮,這個(gè)限制會引發(fā)各種問題;
1.7 性能優(yōu)化
盡量不要在循環(huán)中創(chuàng)建很多臨時(shí)變量眉抬;
可以將大型的循環(huán)拆散贯吓、分段或者按需執(zhí)行;
引入SDK庫和調(diào)用新的系統(tǒng)API里需要考慮成本蜀变;
除了Dalvik堆內(nèi)存悄谐,還有其他類型的內(nèi)存在了解了原理后也能夠進(jìn)行分析和優(yōu)化;
dex 文件有很多優(yōu)化空間库北。在仔細(xì)統(tǒng)計(jì)并調(diào)整了dex文件的順序后爬舰,往往可以節(jié)約1M以上的 mmap 內(nèi)存;
2. 耗電
在保證用戶的必要體驗(yàn)前提下寒瓦,盡可能減少不必要的操作情屹。幾個(gè)優(yōu)化方法:
方法一:CPU時(shí)間片
當(dāng)應(yīng)用退到后臺運(yùn)行時(shí),盡量減少應(yīng)用的主動運(yùn)行杂腰,當(dāng)檢測到CPU時(shí)間片消耗異常時(shí)垃你,深入線程進(jìn)行分析;
使用 DDMS 的 traceview 工具:獲取進(jìn)程運(yùn)行過程中的 traceview,定位CPU占用率異常的方法。
方法二 wake lock
前臺運(yùn)行時(shí)運(yùn)不要去注冊 wake lock惜颇。 此時(shí)注冊沒有任何意義皆刺,卻會被計(jì)算到應(yīng)用電量消耗中。后臺運(yùn)行時(shí)凌摄,在保證業(yè)務(wù)需要的前提下羡蛾,應(yīng)盡量減少注冊 wake lock;降低對系統(tǒng)的喚醒頻率锨亏,使用 partial wake lock 代替 wake lock林说;
方法三 傳感器
合理地設(shè)置 GPS 的使用時(shí)長和使用頻率;
方法四 云省電策略
可考慮定期上報(bào)用戶手機(jī)電量數(shù)據(jù)的方式來分析問題屯伞;
3. 流暢度
3.1 分析工具
hierarchy Viewer ,幫助我們?nèi)シ治鯱I布局的情況豪直;
Tracer for OpenGL ES,可以記錄和分析APP每一幀的繪制過程劣摇,以及列出所有乃至的OpenGL ES 的繪制函數(shù)和耗時(shí);該工具操作后會生成一份記錄App繪制過程和gltrace文件弓乙,
Lint 掃描末融,發(fā)現(xiàn)代碼中的流暢度性能問題;
Traceview,跟蹤程序性能暇韧,具體到每一個(gè)函數(shù)的耗時(shí)和調(diào)用次數(shù)
Systrace 勾习,獲取App運(yùn)行時(shí)線程的信息以及Api執(zhí)行情況
< merge > 標(biāo)簽:用于減少View樹的層次來優(yōu)化 Android 的布局,通過該標(biāo)簽可以把 < merge > 標(biāo)簽里的UI合到上一層的 layout中懈玻。
< ViewStub> 標(biāo)簽巧婶,最大的優(yōu)點(diǎn)是當(dāng)你需要時(shí)才會加載,使用它并不會影響UI初始化時(shí)的性能涂乌。各種不常用的布局可以使用該標(biāo)簽來減少內(nèi)存使用量艺栈,加快渲染速度。< ViewStub> 是一個(gè)不可見的湾盒,大小為0的View湿右。
對于不常用的 UI 可以考慮使用 < ViewStub> 標(biāo)簽替代 GONE 來提高 UI 性能:
將 View 的可見性設(shè)置為 GONE,在 Inflate 布局時(shí) View仍然會被 Inflate罚勾,也就是說仍然會創(chuàng)建對象毅人,會被 實(shí)例化。而 ViewStud 是一個(gè) 輕量級的 View尖殃,它是一個(gè)看不見丈莺、不占布局位置、占用資源非常小的控件分衫。
3.2 Perforjmance中的16個(gè)問題
DrawAllocation: 避免在繪制或者解析布局(draw/layout)時(shí)分配對象场刑,比如在Ondraw()中實(shí)例化 Paint 對象;
Wakelock, 手機(jī)不能進(jìn)入休眠狀態(tài),導(dǎo)致手機(jī)一直保持在高耗電狀態(tài)牵现;
Recycle :某些資源铐懊,比如 TypedArrays 、 VelocityTrackers瞎疼,用完后應(yīng)該被回收科乎,但是忘記回收。
ObsoleteLayoutParam : Layout中無用的參數(shù)贼急;
UseCompoundDrawables,可優(yōu)化的布局茅茂;
HandlerLeak: Handler 的使用不當(dāng)導(dǎo)致內(nèi)存泄漏;
UseSparseArrays ,盡量用 Android 的SparseArray 代替 Hashmap;
UseValueOf : 需要常量對象時(shí)太抓,不應(yīng)該直接 new, 應(yīng)該使用 ValueOf 轉(zhuǎn)換空闲。比如需要整數(shù) 42 的對象,不要直接用 new Integer(42),應(yīng)該用 Intener.vallueOf(42),這樣可以省內(nèi)存走敌;
DisableBaselineAlignment: 如果 LinearLayout 被用于嵌套 layout空間 計(jì)算碴倾,它的 android:baselineAligned 屬性應(yīng)該設(shè)置成 false ,以加速 layout 計(jì)算掉丽;
InefficientWeight : 當(dāng)線性布局里只有一個(gè)控件跌榔,并且使用了weight 屬性,最好把 weidth 和 height 設(shè)置為0捶障,這樣可以省略布局的 measure 過程僧须;
FloatMath, 使用 FloatMath 代替 Math;
NestedWeights : 避免嵌套 weight 项炼,那將拖累執(zhí)行效率担平。
UnusedResources / UnusedIds, 未被使用的資源會使程序變大,并且編譯速度降低锭部;
Overdraw: 如果為 RootView 指定一個(gè)背景 Drawable,會先用Theme 的背景繪制一遍驱闷,然后才用指定的背景,這就是所謂的 “Overdraw” 空免,可以設(shè)置 theme 的background 為 null 來避免空另;
UselessLeaf / UselessParent : View 或 view 的父親沒有用,應(yīng)該把它移除蹋砚,避免影響加深布局的層次扼菠;
UnusedNamespace : 有些代碼沒必要使用 namespace ,會影響代碼執(zhí)行效率;
4. 網(wǎng)絡(luò)優(yōu)化
考慮點(diǎn):
分小片傳輸一個(gè)文件(圖片)坝咐,這樣當(dāng)某一個(gè)分片失敗時(shí)循榆,只需要重傳這一個(gè)分片就可以炼鞠,而不用重傳整個(gè)文件胰耗;
不同類型的移動互聯(lián)網(wǎng)下的分片初始大小應(yīng)該有所不同;
在上傳一個(gè)文件的過程中穗泵,應(yīng)當(dāng)盡可能動態(tài)增大分片大小,以減小分片數(shù)量盗尸;
確定每個(gè)分片是否要繼續(xù)增大之前柑船,要檢查網(wǎng)絡(luò)類型是否發(fā)生了改變;
分片一旦傳輸失敗泼各,應(yīng)當(dāng)使用該網(wǎng)絡(luò)下的初始分片大小進(jìn)行重試鞍时;
重點(diǎn)優(yōu)化優(yōu)質(zhì)網(wǎng)絡(luò)下的傳輸速度,而不特意優(yōu)化差網(wǎng)絡(luò)下的速度扣蜻;
5. apk瘦身
5.1 瘦身關(guān)鍵點(diǎn):
代碼部分:冗余代碼逆巍、無用功能、代碼混淆莽使、方法數(shù)縮減锐极;
資源部分:冗余資源、資源混淆芳肌、圖片處理(壓縮溪烤、圖片轉(zhuǎn)換、點(diǎn)9圖化等)庇勃;
對整個(gè)安裝包做7zip極限壓縮;
Android 系統(tǒng)安裝一個(gè)應(yīng)用的過程中槽驶,其中有一步是對 Dex 進(jìn)行優(yōu)化责嚷,優(yōu)化的過程是使用專門的工具 DexOpt。DexOpt 是在第一次加載Dex文件的時(shí)候執(zhí)行的掂铐。在DexOpt的過程會生成一個(gè)ODEX文件罕拂。
早期的 DexOpt 有兩個(gè)問題:
DexOpt 會把每一個(gè)類的方法的id 檢索起來,存在一個(gè)鏈表的結(jié)構(gòu)里的全陨,但是這個(gè)鏈表的長度是用一個(gè) short 類型來保存的爆班,導(dǎo)致了方法 id 的數(shù)目不能超過 65536(2^16);
DexOpt 使用 LinearAlloc 來存儲應(yīng)用的方法信息,LinearAlloc是一個(gè)固定大小的緩沖區(qū)(4辱姨,5柿菩,8,16)雨涛,當(dāng)方法數(shù)量過多也會導(dǎo)致超出緩沖區(qū)大小時(shí)枢舶,也會造成 DexOpt 崩潰;
5.2 縮減方法數(shù)的方法
避免在內(nèi)部類中訪問外部類的私有方法或變量替久;當(dāng)在 java 內(nèi)部類(包括 匿名內(nèi)部類)中訪問 外部類的私有方法或變量時(shí)凉泄,編譯器會生成額外的方法;
避免調(diào)用派生類中的未被覆蓋( override) 的方法蚯根;避免在派生類中調(diào)用未覆蓋的基類的方法后众;避免用派生為對象調(diào)用派生類中未被覆蓋的基類的方法。因?yàn)楫?dāng)調(diào)用派生類中的未被覆蓋的方法時(shí),會多產(chǎn)生一個(gè)方法數(shù)蒂誉;
去掉部分類的get 教藻、set 方法;
5.3 代碼混淆
代碼混淆( Obfuscated code)也叫花指令;對代碼進(jìn)行 Proguard 后拗盒,也可以比較大的減小代碼的體積(即 dex 的體積)怖竭;