性能問題分類
性能優化原則和方法
藉助性能優化工具分析解決問題
性能優化指標
性能問題分類1、渲染問題: 過度繪製、布局冗雜
2、內存問題: 內存浪費(內存管理)、內存洩漏
3、功耗問題: 耗電
性能優化原則和方法1、性能優化原則
堅持性能測試(開發和測試同學的測試方法略有不同):不要憑感覺去檢測性能問題、評估性能優化的效果,應該保持足夠多的測量,用數據說話(主要針對測試同學)。使用各種性能工具測試及快速定位問題(主要針對開發同學)。
使用低配置的設備:同樣的程序,在低端配置的設備中,相同的問題會暴露得更為明顯。
權衡利弊:在能夠保證產品穩定、按時完成需求的前提下去做優化。
2、優化方法
了解問題(分為可感知和不可感知的性能問題):對於性能問題來講,這個步驟只適用於某些明顯的性能問題,很多無法感知的性能問題需要通過工具定位。例如:內存洩漏、層級冗雜、過度繪製等無法感知。滑動卡頓是可以感知到的。
定位問題:通過工具檢測、分析數據,定位在什麼地方存在性能問題。
分析問題:找到問題後,分析針對這個問題該如何解決,確定解決方案。
解決問題:根據分析結果尋找解決方案。
驗證問題:保證優化有效,沒有產生新的問題,以及產品穩定性。
性能優化工具以下優化工具在下面文章中具體介紹使用方法。
1、手機開發者選項:調試GPU過度繪製、啟用嚴格模式、顯示CPU使用情況、GPU呈現模式分析、顯示所有"應用程式無響應"。(小米手機開發開發者選項中名字)
2、IDE中:Android Studio,比如靜態代碼檢測工具、Memory Monitor、CPU Monitor、NetWork Monitor、GPU Monitor、Layout Inspector、Analyze APK等。
3、SDK中:sdk\tools,比如DDMS、HierarchyViewer、TraceView等。
4、第三方工具:MAT、LeakCanary、GT等。
性能優化指標1、渲染
滑動流暢度:FPS,即Frame per Second,一秒內的刷新幀數,越接近60幀越好;
過度繪製:單頁面的3X(粉紅色區域) Overdraw小於25%
啟動時間:這裡主要說的是Activity界面啟動時間,一般低於300ms,需要用高頻攝像機計算時間。
2、內存
內存大小:峰值越低越好,需要優化前後做對比
內存洩漏:需要用工具檢查對比優化前後
3、功耗
一、渲染問題先來看看造成應用UI卡頓的常見原因都有哪些?
1、人為在UI線程中做輕微耗時操作,導致UI線程卡頓;
2、布局Layout過於複雜,無法在16ms內完成渲染;
3、同一時間動畫執行的次數過多,導致CPU或GPU負載過重;
4、View過度繪製,導致某些像素在同一幀時間內被繪製多次,從而使CPU或GPU負載過重;
5、View頻繁的觸發measure、layout,導致measure、layout累計耗時過多及整個View頻繁的重新渲染;
6、內存頻繁觸發GC過多(同一幀中頻繁創建內存),導致暫時阻塞渲染操作;
7、冗餘資源及邏輯等導致加載和執行緩慢;
8、臭名昭著的ANR;
大多數用戶感知到的卡頓等性能問題的最主要根源都是因為渲染性能。(Google官方說的)
Android系統每隔16ms發出VSYNC信號(vertical synchronization --場掃描同步,場同步,垂直同步),觸發對UI進行渲染,如果每次渲染都成功,這樣就能夠達到流暢的畫面所需要的60fps,為了能夠實現60fps,這意味著程序的大多數操作都必須在16ms(1000/60=16.67ms)內完成。
如果你的某個操作花費時間是24ms,系統在得到VSYNC信號的時候就無法進行正常渲染,這樣就發生了丟幀現象。那麼用戶在32ms內看到的會是同一幀畫面。
1、過度繪製Overdraw(過度繪製)描述的是屏幕上的某個像素在同一幀的時間內被繪製了多次。在多層次的UI結構裡面,如果不可見的UI也在做繪製的操作,這就會導致某些像素區域被繪製了多次。這就浪費大量的CPU以及GPU資源,找出界面滑動不流暢、界面啟動速度慢、手機發熱。
如何查看過度繪製?
設置 — 開發中選項 — 調試GPU過度繪製
來看看手雷裡的過度繪製和優化效果(目前手雷還存在很多待優化的頁面)
上圖中的各種顏色都代表什麼意思?
每個顏色的說明如下: 原色:沒有過度繪製 紫色:1 次過度繪製 綠色:2 次過度繪製 粉色:3 次過度繪製 紅色:4 次及以上過度繪製
造成過度優化的關鍵是什麼?多餘的背景(Background)
接下來舉例說明:
1、MainTabActivity
在MainTabActivity的Theme中修改背景
去除布局(main_activity_linerlayout.xml)中的background
如果不給當前Activity設置主題,默認主題是什麼,默認主題背景是什麼?
可以在默認主題中添加通用主題背景 <item name="android:windowBackground">@drawable/common_layout_content_bkg</item> 去除背景 <item name="android:windowBackground">null</item>
2、除了布局中多餘背景,還有可能在代碼裡添加了多餘的背景。
查看分享彈窗的布局代碼發現只有一個background,但為什麼會過度繪製呢?
代碼修改(SharePlatformsDialog.java)
3、彈窗底部布局不會導致彈窗本身過度繪製
彈窗的繪製是屬於剪切式繪製不是覆蓋繪製,蒙層是透明度亮度的調節不是繪製一層灰色。如果我們不想用系統dialog而是自定義一個彈窗view,就需要考慮過度繪製問題。
4、自定義view時,通過Canvas的clipRect方法控制每個視圖每次刷新的區域,這樣可以避免刷新不必要的區域,從而規避過渡繪製的問題。還可以使用canvas.quickreject()來判斷是否和某個矩形相交,從而跳過那些非矩形區域內的繪製操作。參考:jaeger.itscoder.com/android/201…
優化方法和步驟關鍵總結
總結一下,優化步驟如下: 1、移除或修改Window默認的Background 2、移除XML布局文件中非必需的Background 3、按需顯示佔位背景圖片 4、控制繪製區域
2、布局優化布局太過複雜,層級嵌套太深導致繪製操作耗時,且增加內存的消耗。
我們的目標就是,層級扁平化。
3、介紹一下查看渲染性能的工具二、內存問題1、內存浪費程序內存的管理是否合理高效對應用的性能有著很大的影響。
推薦閱讀Android性能優化典範-第3季,參考:hukai.me/android-per…
1、對象個數的數量級最好是千以內,沒有頻繁的插入刪除操作 2、數據組織形式包含Map結構
Autoboxing(避免自動裝箱)
Autoboxing的行為還經常發生在類似HashMap這樣的容器裡面,對HashMap的增刪改查操作都會發生了大量的autoboxing的行為。當key是int類型的時候,HashMap和ArrayMap都有Autoboxing行為。
SparseArray(項目中用到較多 -- 後面再說如何利用工具查找該用SparseArray而沒有用到的地方)
為了避免Autoboxing行為Android提供了SparseArray,此容器使用於key為int類型。
SparseBooleanMap,SparseIntMap,SparseLongMap等容器,是key為int,value類型相應為boolean、int、long等。
Enum(枚舉,項目中較多使用,應儘量避免)
Enums often require more than twice as much memory as static constants. You should strictly avoid using enums on Android.
Android官方強烈建議不要在Android程序裡面使用到enum。
關於enum的效率,請看下面的討論。假設我們有這樣一份代碼,編譯之後的dex大小是2556 bytes,在此基礎之上,添加一些如下代碼,這些代碼使用普通static常量相關作為判斷值:
增加上面那段代碼之後,編譯成dex的大小是2680 bytes,相比起之前的2556 bytes只增加124 bytes。假如換做使用enum,情況如下:
使用enum之後的dex大小是4188 bytes,相比起2556增加了1632 bytes,增長量是使用static int的13倍。不僅僅如此,使用enum,運行時還會產生額外的內存佔用,如下圖所示:
推薦一些文章:
HashMap,ArrayMap,SparseArray源碼分析及性能對比,參考:www.jianshu.com/p/7b9a1b386…
Android性能優化--小心自動裝箱:blog.csdn.net/lgz_ei/arti…
Android性能優化篇:Android中如何避免創建不必要的對象:blog.csdn.net/jia635/arti…
HashMap、ArrayMap、SparseArray分析比較:blog.csdn.net/chen_lifeng…
Android性能優化之String篇:www.androidchina.net/5940.html
SharedPreferences的commit和apply分析:blog.csdn.net/u010198148/…
2、內存洩漏什麼是內存洩漏?
一些不用的對象被長期持有,導致內存無法被釋放。
可能發生內存洩漏的地方有哪些?
內部類引用導致Activity的洩漏
在Java中,非靜態(匿名)內部類會默認隱性引用外部類對象。而靜態內部類不會引用外部類對象。
最典型的場景是Handler導致的Activity洩漏,如果Handler中有延遲的任務或者是等待執行的任務隊列過長,都有可能因為Handler繼續執行而導致Activity發生洩漏。
為了解決這個問題,可以在UI退出之前,執行remove Handler消息隊列中的消息與runnable對象。或者是使用Static + WeakReference的方式來達到斷開Handler與Activity之間存在引用關係的目的。
舉例,MainTabActivity - MainTabHandler:
如何修復?
Android Weak Handler:可以避免內存洩漏的Handler庫,參考:www.jcodecraeer.com/a/anzhuokai…
Activity Context被傳遞到其他實例中,這可能導致自身被引用而發生洩漏。
考慮使用Application Context而不是Activity Context。
例如:全局Dialog或者Context被單例持有。
靜態造成的內存洩漏
還有靜態變量持有View,例如:
private static View view; void setStaticView() { view = findViewById(R.id.sv_button); }
注意監聽器的註銷(稍後利用工具分析一個例子)
regist就要unregist
注意Cursor對象是否及時關閉(項目中也存在,不再列舉)
WebView的引起的洩漏(暫時沒有研究)
使用工具分析定位解決內存洩漏
內存使用策略優化
onLowMemory():Android系統提供了一些回調來通知當前應用的內存使用情況,通常來說,當所有的background應用都被kill掉的時候,forground應用會收到onLowMemory()的回調。在這種情況下,需要儘快釋放當前應用的非必須的內存資源,從而確保系統能夠繼續穩定運行。
onTrimMemory(int):Android系統從4.0開始還提供了onTrimMemory()的回調,當系統內存達到某些條件的時候,所有正在運行的應用都會收到這個回調,同時在這個回調裡面會傳遞參數,代表不同的內存使用情況,收到onTrimMemory()回調的時候,需要根據傳遞的參數類型進行判斷,合理的選擇釋放自身的一些內存佔用,一方面可以提高系統的整體運行流暢度,另外也可以避免自己被系統判斷為優先需要殺掉的應用。
看看下載一個視頻加上瀏覽一下精選頁,然後將應用切到後臺,內存使用情況
有什麼優化內存的策略
文章推薦:
Android內存優化之OOM:hukai.me/android-per…
內存洩露從入門到精通三部曲之基礎知識篇:bugly.qq.com/bbs/forum.p…
內存洩露從入門到精通三部曲之排查方法篇:bugly.qq.com/bbs/forum.p…
內存洩露從入門到精通三部曲之常見原因與用戶實踐:bugly.qq.com/bbs/forum.p…
胡凱Android性能典範系列
3、性能優化必備神器推薦(Lint)上面分析的一些項目中的問題,怎麼找到的呢?
Lint:靜態代碼分析工具
如何通過Lint查找項目中的問題,如何使用?
如果只想分析某個文件夾的代碼
設置代碼分析選項
作者:李通
連結:https://juejin.im/post/5ad2bfaf51882555867fdd09
來源:掘金
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。