移動APP性能評測與優化


本文是《移動App性能評測與優化》的讀書筆記。
PS:說是讀書筆記,其實就是摘錄。

移動App的性能測試主要包括:內存使用情況、電量消耗、功能的流暢度等;

1. 內存

1.1 內存的主要組成索引:

  • Native Heap : Native 代碼分配的內存,虛擬機和Android框架本身也會分配;

  • Dalvik Heap : Java 代碼分配的對象;

  • Dalvik Other : 類的數據結構和索引;

  • so mmap : Native 代碼和常量;

  • dex mmap :Java 代碼和常量;

1.2 內存測試工具

  • Android Studio / Memory Monitor : 觀察 Dalvik 內存;

  • dumpsys meminfo : 觀察整體內存;

  • smaps : 觀察整體內存的詳細組成;

  • MAT : 詳細分析 Dalvik內存;

1.3 一個類的內存消耗

虛擬機在創建對象時的操作:

  • loadClass,將類信息從 dex 文件中加載進內存:

    • 讀取 .dexx mmap 中 class 對應的數據;

    • 分配 native-heap 和 dalvik-heap 內存創建 class 對象;

    • 分配 dalvik-LinearAlloc 存放 class 數據;

    • 分配 delvik-aux-structure 存放 class 數據;

  • new instance 操作,創建對象實例

    • 執行 .dex mmap 中 的代碼;

    • 分配 delvik-heap 創建 class對象實例;

dex mmap 在Android應用中的作用是映射classes.dex文件

1.4 dex 優化

省略掉dex的文件結構(自行查閱)

為了節省空間,dex將原先在 class 文件中重復的信息集中放置在一起,並以索引和指針的形式支持快速訪問。

dex 文件中數據基本是按類名的字母順序進行排列的,這樣同樣包名的類會排在一起,但程序實際執行時,同一個包下的類並不會全部調用到,而是跨包進行交互,但 mmap 加載了整個頁面,可能會有很多無用的數據。

優化;

在APK的編譯流程中,Proguard 混淆工具正好是能夠對類名進行修改的,可以根據程序運行時的邏輯,將那些會互相調用的類改為為同一個 package 名,這樣就可以使它們的數據排布在一起。

1.5 MAT(Memory Analyzer Tool)

使用MAT來分析應用的內存使用情況。通常在使用MAT打開hprof文件后,能夠在首頁看到Top Comnsumers和 component Report等功能,我們可以快速定位一些大塊的內存消耗。但我們在分析時會發現系統資源類占據了很大一部分內存,因此為去除這部分對分析的干擾,我們在使用AndroidSDK提供的hprof-conv轉換時需要增加一個參數:


hporf- conv [-z] <infile><outfile> -z:exclude non-app heaps,such as Zygote

如果hprof文件是已經轉換過的,則可以使用OQL:


//在數據中尋找應用的Application類對象,將對象地址轉換為十進制后輸入以下查詢語句:

select * from instanceof java.langObject s where s.@objectAddress> 1107296256

//(后面那串數字應該是Application類對象的地址)

采用這兩種方法后,再使用MAT來分析就可以比較容易發現自身代碼的內存問題。

1.6 測試經驗

  • MAT 是探索 Java 堆並發現問題和好幫手,能夠迅速發現常見的圖片和大數組等問題;

  • 內存碎片問題一般隱藏在對象的地址中;

  • 如需要測試非 Dalvik部分,有必要了解 Linux 的進程和內存原理、內存共享機制,熟悉常用命令行工具;

  • 內存分配的最小單位是頁面,通常為4KB,這個限制會引發各種問題;

1.7 性能優化

  • 盡量不要在循環中創建很多臨時變量;

  • 可以將大型的循環拆散、分段或者按需執行;

  • 引入SDK庫和調用新的系統API里需要考慮成本;

  • 除了Dalvik堆內存,還有其他類型的內存在了解了原理后也能夠進行分析和優化;

  • dex 文件有很多優化空間。在仔細統計並調整了dex文件的順序后,往往可以節約1M以上的 mmap 內存;

2. 耗電

在保證用戶的必要體驗前提下,盡可能減少不必要的操作。幾個優化方法:

方法一:CPU時間片

當應用退到后台運行時,盡量減少應用的主動運行,當檢測到CPU時間片消耗異常時,深入線程進行分析;

使用 DDMS 的 traceview 工具:獲取進程運行過程中的 traceview,定位CPU占用率異常的方法。

方法二 wake lock

前台運行時運不要去注冊 wake lock。 此時注冊沒有任何意義,卻會被計算到應用電量消耗中。后台運行時,在保證業務需要的前提下,應盡量減少注冊 wake lock;降低對系統的喚醒頻率,使用 partial wake lock 代替 wake lock;

方法三 傳感器

合理地設置 GPS 的使用時長和使用頻率;

方法四 雲省電策略

可考慮定期上報用戶手機電量數據的方式來分析問題;

3. 流暢度

3.1 分析工具

  • hierarchy Viewer ,幫助我們去分析UI布局的情況;

  • Tracer for OpenGL ES,可以記錄和分析APP每一幀的繪制過程,以及列出所有乃至的OpenGL ES 的繪制函數和耗時;該工具操作后會生成一份記錄App繪制過程和gltrace文件,

  • Lint 掃描,發現代碼中的流暢度性能問題;

  • Traceview,跟蹤程序性能,具體到每一個函數的耗時和調用次數

  • Systrace ,獲取App運行時線程的信息以及Api執行情況

< merge > 標簽:用於減少View樹的層次來優化 Android 的布局,通過該標簽可以把 < merge > 標簽里的UI合到上一層的 layout中。

< ViewStub> 標簽,最大的優點是當你需要時才會加載,使用它並不會影響UI初始化時的性能。各種不常用的布局可以使用該標簽來減少內存使用量,加快渲染速度。< ViewStub> 是一個不可見的,大小為0的View。

對於不常用的 UI 可以考慮使用 < ViewStub> 標簽替代 GONE 來提高 UI 性能:

將 View 的可見性設置為 GONE,在 Inflate 布局時 View仍然會被 Inflate,也就是說仍然會創建對象,會被 實例化。而 ViewStud 是一個 輕量級的 View,它是一個看不見、不占布局位置、占用資源非常小的控件。

3.2 Perforjmance中的16個問題

  • DrawAllocation: 避免在繪制或者解析布局(draw/layout)時分配對象,比如在Ondraw()中實例化 Paint 對象;

  • Wakelock, 手機不能進入休眠狀態,導致手機一直保持在高耗電狀態;

  • Recycle :某些資源,比如 TypedArrays 、 VelocityTrackers,用完后應該被回收,但是忘記回收。

  • ObsoleteLayoutParam : Layout中無用的參數;

  • UseCompoundDrawables,可優化的布局;

  • HandlerLeak: Handler 的使用不當導致內存泄漏;

  • UseSparseArrays ,盡量用 Android 的SparseArray 代替 Hashmap;

  • UseValueOf : 需要常量對象時,不應該直接 new, 應該使用 ValueOf 轉換。比如需要整數 42 的對象,不要直接用 new Integer(42),應該用 Intener.vallueOf(42),這樣可以省內存;

  • DisableBaselineAlignment: 如果 LinearLayout 被用於嵌套 layout空間 計算,它的 android:baselineAligned 屬性應該設置成 false ,以加速 layout 計算;

  • InefficientWeight : 當線性布局里只有一個控件,並且使用了weight 屬性,最好把 weidth 和 height 設置為0,這樣可以省略布局的 measure 過程;

  • FloatMath, 使用 FloatMath 代替 Math;

  • NestedWeights : 避免嵌套 weight ,那將拖累執行效率。

  • UnusedResources / UnusedIds, 未被使用的資源會使程序變大,並且編譯速度降低;

  • Overdraw: 如果為 RootView 指定一個背景 Drawable,會先用Theme 的背景繪制一遍,然后才用指定的背景,這就是所謂的 “Overdraw” ,可以設置 theme 的background 為 null 來避免;

  • UselessLeaf / UselessParent : View 或 view 的父親沒有用,應該把它移除,避免影響加深布局的層次;

  • UnusedNamespace : 有些代碼沒必要使用 namespace ,會影響代碼執行效率;

4. 網絡優化

考慮點:

  • 分小片傳輸一個文件(圖片),這樣當某一個分片失敗時,只需要重傳這一個分片就可以,而不用重傳整個文件;

  • 不同類型的移動互聯網下的分片初始大小應該有所不同;

  • 在上傳一個文件的過程中,應當盡可能動態增大分片大小,以減小分片數量;

  • 確定每個分片是否要繼續增大之前,要檢查網絡類型是否發生了改變;

  • 分片一旦傳輸失敗,應當使用該網絡下的初始分片大小進行重試;

重點優化優質網絡下的傳輸速度,而不特意優化差網絡下的速度;

5. apk瘦身

5.1 瘦身關鍵點:

  • 代碼部分:冗余代碼、無用功能、代碼混淆、方法數縮減;

  • 資源部分:冗余資源、資源混淆、圖片處理(壓縮、圖片轉換、點9圖化等);

  • 對整個安裝包做7zip極限壓縮;

Android 系統安裝一個應用的過程中,其中有一步是對 Dex 進行優化,優化的過程是使用專門的工具 DexOpt。DexOpt 是在第一次加載Dex文件的時候執行的。在DexOpt的過程會生成一個ODEX文件。

早期的 DexOpt 有兩個問題:

  • DexOpt 會把每一個類的方法的id 檢索起來,存在一個鏈表的結構里的,但是這個鏈表的長度是用一個 short 類型來保存的,導致了方法 id 的數目不能超過 65536(2^16);

  • DexOpt 使用 LinearAlloc 來存儲應用的方法信息,LinearAlloc是一個固定大小的緩沖區(4,5,8,16),當方法數量過多也會導致超出緩沖區大小時,也會造成 DexOpt 崩潰;

5.2 縮減方法數的方法

  • 避免在內部類中訪問外部類的私有方法或變量;當在 java 內部類(包括 匿名內部類)中訪問 外部類的私有方法或變量時,編譯器會生成額外的方法;

  • 避免調用派生類中的未被覆蓋( override) 的方法;避免在派生類中調用未覆蓋的基類的方法;避免用派生為對象調用派生類中未被覆蓋的基類的方法。因為當調用派生類中的未被覆蓋的方法時,會多產生一個方法數;

  • 去掉部分類的get 、set 方法;

5.3 代碼混淆

代碼混淆( Obfuscated code)也叫花指令;對代碼進行 Proguard 后,也可以比較大的減小代碼的體積(即 dex 的體積);

6 參考文獻:

1 移動App性能評測與優化


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM