1. 性能優化工具(七)-調試GPU過度繪制 & GPU呈現模式分析
調試GPU過度繪制和GPU呈現模式分析是android手機自帶的分析工具。能比較方便的幫助開發者檢驗是否存在overdraw和卡頓的問題,但是不能幫忙定位問題。
位置:開發者選項
2.1 調試GPU過度繪制
這個工具主要是用來檢查布局中是否存在布局層次過深的問題。
從上圖看到檢測的結果分為四個等級,藍色,淡綠,淡紅,深紅代表了4種不同程度的Overdraw情況,我們的目標就是盡量減少紅色Overdraw,看到更多的藍色區域。
2.2 GPU呈現模式分析
這個工具主要是用來反映界面的繪制情況,查看是否存在耗時問題。
具體渲染原理就不介紹了,本章只是工具介紹。
1)柱狀圖每一根代表一幀
2)隨著界面的刷新,界面上會滾動顯示垂直的柱狀圖來表示每幀畫面所需要渲染的時間,柱狀圖越高表示花費的渲染時間越長。
3)中間有一根綠色的橫線,代表16ms,我們需要確保每一幀花費的總時間都低於這條橫線,這樣才能夠避免出現卡頓的問題。
4)詳細分析下不同顏色的柱狀圖代表的意思:
在Android6.0之前,柱狀圖主要為黃色、紅色、藍色(Swap Buffers,Command Issue,Draw)三類。自從安卓6.0之後,玄學曲線進行了改版,增加至8條數據。新版本GPU呈現分析曲線新增加了(Sync&Upload,Measure&LayoutAnimation,Input Handling,Misc/Vsync Delay)五大步驟數據。
(1)Command Issue(紅色):表示執行任務的時間,是Android進行2D渲染顯示列表的時間,為了將內容繪制到屏幕上,Android需要使用Open GL ES的API介面來繪制顯示列表,紅色線條越高表示需要繪制的視圖更多;比如我們在遇到多張圖載入的時候,紅色會突然跳的很高,此時滑動頁面也就不流暢了,要等幾秒圖片才能載入出來,並不是卡住。
(2)Swap Buffers(黃色):表示處理任務的時間,即CPU等待GPU完成任務的時間,線條越高,表示GPU做的事情越多。若橙色部分過高,說明GPU目前過於忙碌。
(3)Draw(藍色):表示測量和繪制視圖列表所需要的時間,藍色線條越高表示每一幀需要更新很多視圖,或者View的onDraw方法中做了耗時操作。它越長說明當前視圖比較復雜或者無效需要重繪,表現為卡頓。
理想的流暢狀態是三色都低於綠線以下。
(4)Sync & Upload(淺藍色):表示的是准備當前界面上有待繪制的圖片所耗費的時間,為了減少該段區域的執行時間,我們可以減少屏幕上的圖片數量或者是縮小圖片的大小。
下面這幾種統稱為綠色,隨著後面標注的數字顏色逐漸加深。
(5) Measure/Layout(綠色1)表示布局的onMeasure與onLayout所花費的時間,一旦時間過長,就需要仔細檢查自己的布局是不是存在嚴重的性能問題;。
(6)Animation(綠色2):表示計算執行動畫所需要花費的時間,包含的動畫有ObjectAnimator,ViewPropertyAnimator,Transition等等。一旦這里的執行時間過長,就需要檢查是不是使用了非官方的動畫工具或者是檢查動畫執行的過程中是不是觸發了讀寫操作等等。
(7)Input Handling(綠色3):表示系統處理輸入事件所耗費的時間,粗略等於對事件處理方法所執行的時間。一旦執行時間過長,意味著在處理用戶的輸入事件的地方執行了復雜的操作。
(8) Misc Time/Vsync Delay(綠色4):表示在主線程執行了太多的任務,導致UI渲染跟不上vSync的信號而出現掉幀的情況。
總結一下:
紅色/黃色/:從布局構建角度去考慮。優化:否減少視圖層級、減少無用的背景圖、減輕自定義控制項復雜度等。
藍色/淺藍/各種綠色:從耗時操作角度去考慮。
2. Android性能優化第(八)篇---App啟動速度優化之耗時檢測處理
應用的啟動速度緩慢這是很多開發者都遇到的一個問題,比如啟動緩慢導致的黑屏,白屏問題,大部分的答案都是做一個透明的主題,或者是做一個Splash界面,但是這並沒有從根本上解決這個問題。那麼如何從根本上解決這個問題或者做到一定程度的緩解?
1、冷啟動:當啟動應用時,後台沒有該應用的進程,這時系統會首先會創建一個新的進程分配給該應用,這種啟動方式就是冷啟動。
2、熱啟動:當啟動應用時,後台已有該應用的進程,比如按下home鍵,這種在已有進程的情況下,這種啟動會從已有的進程中來啟動應用,這種啟動方式叫熱啟動。
3、溫啟動 :當啟動應用時,後台已有該應用的進程,但是啟動的入口Activity被幹掉了,比如按了back鍵,應用雖然退出了,但是該應用的進程是依然會保留在後台,這種啟動方式叫溫啟動。
adb shell am start -W [PackageName]/[PackageName.MainActivity]
執行成功後將返回三個測量到的時間:
這裡面涉及到三個時間,ThisTime、TotalTime 和 WaitTime。WaitTime 是 startActivityAndWait 這個方法的調用耗時,ThisTime 是指調用過程中最後一個 Activity 啟動時間到這個 Activity 的 startActivityAndWait 調用結束。TotalTime 是指調用過程中第一個 Activity 的啟動時間到最後一個 Activity 的 startActivityAndWait 結束。如果過程中只有一個 Activity ,則 TotalTime 等於 ThisTime。
總結:如果只關心某個應用自身啟動耗時,參考TotalTime;如果關心系統啟動應用耗時,參考WaitTime;如果關心應用有界面Activity啟動耗時,參考ThisTime。
從我們Application開始到首頁顯示出來,這個過程,我們應該注意一些什麼,將這個過程細分一下,會有下面的時間點需要注意。
Application的構造器方法——>attachBaseContext()——>onCreate()——>Activity的構造方法——>onCreate()——>配置主題中背景等屬性——>onStart()——>onResume()——>測量、布局、繪制顯示在界面上。
因為上面這些階段全部都是在主線程中執行的,任何不經意的操作都可能拖慢應用的啟動速度。所以我們不應在Application以及Activity的生命周期回調中做任何費時操作,具體指標大概是你在onCreate,onResume,onStart等回調中所花費的總時間最好不要超過400ms,否則用戶在桌面點擊你的應用圖標後,將感覺到明顯的卡頓。但是有些 不得以的任務 又必須在UI顯示之前執行。所以我們要將 任務 劃分優先順序。
對於首頁渲染完成後,開始載入,或者延遲載入,延遲載入的目的就是界面先顯示出來,然後載入,但是你覺得要延遲多久呢?在 Android 的高端機型上,應用的啟動是非常快的 , 這時候只需要 Delay 很短的時間就可以了, 但是在低端機型上,應用的啟動就沒有那麼快了,而且現在應用為了兼容舊的機型,往往需要 Delay 較長的時間,這樣帶來體驗上的差異是很明顯的。延遲載入有一種方式。
極力推薦用第二種,在窗口完成以後進行載入,這裡面的run方法是在onResume之後運行的。關於這種懶載入機制,參考 Android應用啟動優化:一種DelayLoad的實現和原理(上篇) ,給出了詳細的解釋。
通過上面我們知道一種懶載入機制,所以我們可以將Application中和首頁的onCreate中的有些耗時任務,放到首頁渲染完畢後載入。如何找出這些耗時任務,TraceView就派上用場了,TraceView的用法,移步我的前面的博客 Android性能優化第(六)篇---TraceView 分析圖怎麼看?
比如在首頁的onCreate中我們進行了用戶啟動上報,這個進行懶載入是不是分分鍾減少139毫秒呢?
在比如在Application裡面用到了GSON,將String轉化成json,我將這個移動到懶載入裡面,是不是又減少了100毫秒呢?
在比如,有些Application中做了支付SDK的初始化,用戶又不會一打開App就要支付,放在Application中載入幹嘛?
此處我們這里舉得例子是優化了139毫秒和100毫秒的,其實真正耗時的任務有的有1秒多,都被我優化完了,所以trace圖中看不到了,就舉個了這兩個例子,還有SharedPreferences也是耗時大戶,經過檢測保存一個boolean變數耗時120+毫秒以上。
利用TraceView可以清楚我們每一個方法的耗時時間,極大的幫助了我們做優化工作。
五、優化思路總結
1、UI渲染優化,去除重復繪制,減少UI重復繪制時間,打開設置中的GPU過度繪制開關,各界面過度繪制不應超過2.5x;也就是打開此調試開關後,界面整體呈現淺色,特別復雜的界面,紅色區域也不應該超過全屏幕的四分之一;
2、根據優先順序的劃分,KoMobileApplication的一些初始化工作能否將任務優先順序劃分成3,在首頁渲染完成後進行載入,比如:PaySDKManager。
3、主線程中的所有SharedPreference能否在非UI線程中進行,SharedPreferences的apply函數需要注意,因為Commit函數會阻塞IO,這個函數雖然執行很快,但是系統會有另外一個線程來負責寫操作,當apply頻率高的時候,該線程就會比較佔用CPU資源。類似的還有統計埋點等,在主線程埋點但非同步線程提交,頻率高的情況也會出現這樣的問題。
4、檢查BaseActivity,不恰當的操作會影響所有子Activity的啟動。
5、對於首次啟動的黑屏問題,對於「黑屏」是否可以設計一個.9圖片替換掉,間接減少用戶等待時間。
6、對於網路錯誤界面,友好提示界面,使用ViewStub的方式,減少UI一次性繪制的壓力。
7、任務優先順序為2,3的,通過下面這種方式進行懶載入的方式
8、Multidex的使用,也是拖慢啟動速度的元兇,必須要做優化。後面有空專門寫一篇Multidex。
相關鏈接:
Android應用啟動優化:一種DelayLoad的實現和原理(上篇)http://androidperformance.com/2015/11/18/Android-app-lunch-optimize-delay-load.html
Android性能優化之加快應用啟動速度http://www.open-open.com/lib/view/open1452821612355.html
手機淘寶性能優化全記錄http://www.open-open.com/lib/view/open1452488209370.html
Android客戶端性能優化(魅族資深工程師毫無保留奉獻)http://blog.tingyun.com/web/article/detail/155#rd
Please accept mybest wishes for your happiness and success !
3. androidUI卡頓原理分析及Vsync信號機制
一、UI卡頓定義
1、用戶角度:app操作界面刷新緩慢,響應不及時;界面滑動不夠流暢;
2、系統角度:屏幕刷新幀率不穩定,掉幀嚴重,無法保證每秒60幀,導致屏幕畫面撕裂;
二、UI卡頓常見原因分析以及處理方案
1、過度繪制:
原因:界面布局設計不合理或者過於復雜導致系統無法在16毫秒內完成渲染,view過度繪制導致CPU或者GPU負載過重,View頻繁觸發measure、layout操作,導致measure、layout累計耗時嚴重以及整個View錯誤的頻繁重新渲染;
方案:優化界面布局,使界面布局視圖扁平化,去除不必要的背景顏色,減少透明色的使用;
方案依據原理:盡量減少View在系統中measure、layout、draw的累計時間;
2、UI線程的復雜運算
原因:UI主線程運算耗時
方案:減少UI線程中數據運算,使用子線程處理耗時任務
3、頻繁GC
原因:(1)、內存抖動;(2)、瞬間產生大量對象,消耗內存;
方案:盡量避免在循環邏輯或者onDraw方法中頻繁創建新對象和使用局部變數;
三、android Vsync機制
1、什麼是Vsync ?
Vsync 是Vertical Synchronization(垂直同步)的縮寫,是一種在PC上很早就廣泛使用的技術,可以簡單的把它認為是一種定時中斷。而在Android 4.1(JB)中已經開始引入VSync機制,用來同步渲染,讓AppUI和SurfaceFlinger可以按硬體產生的VSync節奏進行工作。
2、Android屏幕刷新過程
Android系統每隔16ms發出VSYNC信號,觸發對UI進行渲染,屏幕的刷新過程是每一行從左到右(行刷新,水平刷新,Horizontal Scanning),從上到下(屏幕刷新,垂直刷新,Vertical Scanning)。當整個屏幕刷新完畢,即一個垂直刷新周期完成,會有短暫的空白期,此時發出 VSync 信號。所以,VSync 中的 V 指的是垂直刷新中的垂直-Vertical。
3、沒有使用Vsync的情況
可見vsync信號沒有提醒CPU/GPU工作的情況下,在第一個16ms之內,一切正常。然而在第二個16ms之內,幾乎是在時間段的最後CPU才計算出了數據,交給了Graphics Driver,導致GPU也是在第二段的末尾時間才進行了繪制,整個動作延後到了第三段內。從而影響了下一個畫面的繪制。這時會出現Jank(閃爍,可以理解為卡頓或者停頓)。這時候CPU和GPU可能被其他操作佔用了,這就是卡頓出現的原因;
4、使用Vsync同步
CPU/GPU接收vsync信號,Vsync每16ms一次,那麼在每次發出Vsync命令時,CPU都會進行刷新的操作。也就是在每個16ms的第一時間,CPU就會響應Vsync的命令,來進行數據刷新的動作。CPU和GPU的刷新時間,和Display的FPS是一致的。因為只有到發出Vsync命令的時候,CPU和GPU才會進行刷新或顯示的動作。CPU/GPU接收vsync信號提前准備下一幀要顯示的內容,所以能夠及時准備好每一幀的數據,保證畫面的流暢;
5、多級緩沖
Android除了使用Vsync機制,還使用了多級緩沖的策略來優化屏幕顯示,如雙重緩沖(A + B),當Display buffer A 數據時,CPU/GPU就已經在buffer B 中處理下一幀要顯示的數據了。
可是,當系統資源緊張性能降低時,導致GPU在處理某幀數據時太耗時,在Vsync信號到來時,buffer B的數據還沒准備好,此時不得不顯示buffer A的數據,這樣導致後面CPU/GPU沒有新的buffer准備數據,空白時間無事可做,後面Jank頻出
因此採用三級緩沖來解決系統對性能不穩定導致的卡頓
當出現上面所述情況後,新增一個buffer C 可以減少CPU和GPU在Vsync同步間的空白間隙,此時CPU/GPU能夠利用buffer C 繼續工作,後面buffer A 和 buffer B 依次處理下一幀數據。這樣僅是產生了一個Jank,可以忽略不計,以後的流程就順暢了。
註:在多數正常情況下還是使用二級緩沖機制,三級緩沖只是在需要的時候才使用;
4. 過度繪制的解決
《Google的性能優化典範》一文是Android程序內存優化的指導,分別從渲染、電量、運算和內存幾個方面闡述了優化方向。
本文關注渲染方向:
渲染其實是指GPU渲染,是App計算--繪制--渲染 過程中的最後一步。CPU負責Measure Layout,Execute GPU負責Rasterization(柵格化)。
CPU通常存在的問題是 非必需的視圖組件、視圖層級;GPU的問題是過度繪制。
Overdraw 過度繪制:
定義:屏幕上的某個像素在同一幀的時間內被繪制了多次
例如UI是層疊的,看不見的UI也做繪制操作,就是多餘的。當設計效果上更加華麗炫酷時,堆疊視圖層級是常見的情況,但這很容易產生性能問題。
怎麼過度繪制打開開關和如何看,不介紹了就。
1.寫合理而高效的布局
Android的布局可以通過xml來實現,這使得開發者布局時較為隨意,只以實現功能為目的,忽略性能問題的累積效應。
在開發設計之初,就應該考慮布局的效率問題,以免出現後期修改的高成本。
降低Layout層級,有很多方法 不列舉了。
2.移除非必須的background: Activity的DecorView有默認的背景色,可以改為透明
getWindow().getDecorView().setBackgroundColor(getResources().getColor(R.color.transparent));
這個顏色從ActivityTheme設置,被decorView所持有
screen_background_selector_dark在sdk中定義為純黑色
所以也可以 android:windowbackground="null" 方法來修改
後續會在Theme自定義,或BaseActivity 統一優化
3.View BackGround 優化:
4.移除不必要的背景色
比如Activity中含Fragment,如果Fragment有背景色而且是全屏的,Activity就不必要。
又比如ViewPager中含fragment ViewPager的背景色是不必要的
5.ClipRect
在ViewGroup的drawChild方法中,
protected boolean drawChild(Canvas canvas, View child, long drawingTime)
在ViewGroup的Canvas上繪制子child,不同的child都在同一個canvas繪制,如果view相互遮蓋,則重復繪制難免。
Canvas的clipRect方法,提供了限定繪制區域的功能,在某個child 繪制時,可以限定繪制區域為自己的顯示區域,解決了這個問題。
v4包中的DrawerLayout,就專門做了ClipRect優化
pilot端的問題就在於DrawerContent沒有背景,而是把背景設置在了裡面的Fragment,導致DrawerLayout優化沒有生效
此優化一般用於自定義view中,而且控制項交互存在View之間重疊的情況
Android中每個Window對應一個Canvas,window下所有view繪制公用一個canvas,viewtree的父節點在調用child.draw之前都會根據child的layout邊界對canvas進行裁剪,這也是為什麼超過view邊界的內容不會被顯示的原因。
但是對於各child大部分重疊的控制項,會產生過度繪制,就需要clipRect優化。大部分容易重疊的控制項FrameLayout RelativeLayout本身沒有優化,需要開發者根據實際情況對自定義控制項進行優化。
優化前:[圖片上傳失敗...(image-5fc76c-1513077609721)]
優化後:[圖片上傳失敗...(image-87aa6e-1513077609721)]
6.善用9patch,背景圖如果只顯示邊框,選用9patch,中間的透明會被2D渲染器優化overdraw
過度繪制的原因無外乎:復雜的Layout層級、重疊的背景、重疊的View幾種。開發人員在設計之初就要充分考慮過度繪制等性能敏感地帶,要知道等到功能實現之後再去改Layout層級,onDraw方法等,成本和風險都會指數型提高。