在手機App競爭愈來愈激烈的今天,Android App的各項性能特別是流暢度不如IOS,安卓基於java虛擬機運行,觸控響應的延遲和卡頓比IOS系統嚴重得多。一些下拉上滑、雙指縮放快速打字等操做,安卓的流暢度都表現比較糟糕,可是,對於App使用過程是否流暢,一直沒有一個可靠的指標將用戶的客觀感覺和數據一一對應。雖然以前有FPS(每秒幀數)做爲遊戲或視頻類App的性能指標,但對於那些界面更新很少的App來講,仍不是一個合適的衡量數據。如下會根據實際app性能測試案例,展開進行app性能評測之流暢度進行原理分析和評測總結。html
刷新率:每秒屏幕刷新次數,手機屏幕的刷新率是60HZ
幀率:GPU在一秒內繪製的幀數java
由於屏幕的刷新過程是自上而下、自左向右的,若是幀率>刷新率,當屏幕尚未刷新n-1幀的數據時,就開始生成第n幀的數據了,從上到下,覆蓋第n-1幀。若是此時刷新屏幕,就會出現圖像的上半部分是第n幀的,下半部分是第n幀的現象。CPU/GPU一直都在渲染。android
Android系統每隔16ms發出VSYNC信號,觸發GPU對UI進行渲染,若是你的某個操做花費時間是24ms,系統在獲得VSYNC信號的時候因爲尚未準備好,就沒法進行更新任何內容,那麼用戶在32ms內看到的會是同一幀畫面(卡頓現象),即丟幀現象。git
GPU向緩存中寫入數據,屏幕從緩存中讀取數據,刷新後顯示。因爲刷新率和幀率並不老是一致的,極可能致使撕裂的現象。爲了解決單緩存的畫面撕裂問題,出現了雙緩存和 VSync 。github
雙緩存使用了兩個緩存區: Back Buffer 、 Frame Buffer。當寫入下一幀時,GPU會先填充 Back Buffer 中,當刷新屏幕時,屏幕從 Frame Buffer 中讀數據。VSYNC 主要是完成幀的複製,開始下一幀的渲染。 shell
當幀率大於刷新頻率時,經過使幀率被迫跟刷新頻率保持同步,從而避免畫面撕裂的現象(只有當 VSync 信號產生時, CPU/GPU 纔會開始繪製)。當VSync 信號產生時,先完成Back Buffer 到 Frame Buffer的複製操做(經過交換內存地址),而後通知 CPU/GPU 繪製下一幀圖像。也只有VSync 信號發生時,才繪製下一幀。數據庫
當刷新頻率>幀率時,此時刷新屏幕,發出VSYNC 信號,因爲CPU/GPU的渲染操做尚未完成,就不把Back Buffer的數據複製到 Frame Buffer,此時就從Frame Buffer去取舊數據,這樣在兩個刷新週期裏,顯示的是同一幀數據。android-studio
雙重緩存的缺陷在於:當 CPU/GPU 繪製一幀的時間超過 16 ms 時,會產生 Jank。更要命的是,產生 Jank 的那一幀的顯示期間,GPU/CPU 都是在閒置的。
以下圖,A、B 和 C 都是 Buffer。
若是有第三個 Buffer 能讓 CPU/GPU 在這個時候繼續工做,那就徹底能夠避免第二個 Jank 的發生了!緩存
在Android版本更新過程當中,發如今Jelly Bean中Google加入了一個Project Butter,用來解決嚴重影響Android口碑的問題之一「UI流暢性差」的問題。
而Project Butter中主要引入了三個核心元素:VSYNC(垂直同步)、Triple Buffer和Choreographer。
VSync是VerticalSynchronization(垂直同步)的縮寫,是一種在PC上很早就普遍使用的技術,能夠簡單的把它認爲是一種定時中斷。而在Android 4.1(JB)中已經開始引入VSync機制。CPU和GPU的處理時間都少於一個VSync的間隔,即16.6ms。若是每一個間隔都有繪製的狀況下,當前的FPS即爲60幀。VSync機制就像是播放動畫片(60幀/s)。每次都會播放畫面,有的時候有人偷懶了,機器壞了,就會出現播放速度下降的情況。咱們把這個播放速度叫作流暢度。性能優化
FPS即Frames per second,>>點擊這篇文章,解釋的很是清楚。
用過flash的人應該知道動畫片實際上是由一張張畫出來的圖片連貫執行產生的效果,當一張張獨立的圖片切換速度足夠快的時候,會欺騙咱們的眼睛,覺得這是連續的動做。反之類推,當你的圖片切換不夠快的時候,就會被人眼看穿,反饋給用戶的就是所謂的卡頓現象。
想要讓大腦以爲動做是連續的,至少是每秒10-12幀的速度,而想達到流暢的效果,至少須要每秒24幀。這也是爲何電影片源一般都是24幀的緣由,好奇的同窗點擊>>知乎高知
看看大神的解答。不過60幀每秒的流暢度是最佳的,咱們的目標就是讓程序的流暢度能接近60幀每秒,固然超過60幀速的話大部分人仍是會受不了的。
系統獲取FPS的原理是:1s 內 SurfaceFLinger 提交到屏幕的幀數。
計算公式:1000ms / 60 frames ≈ 16.67 ms/frames
原來的測試產品的流暢度,FPS是一個重要的指標,可是用了一段時間後,人們就發現了這樣兩個問題
Question:
1)爲何有時候FPS很低,可是咱們卻不以爲App卡頓?
2)App中止操做以後,FPS仍是一直在變化,這樣的狀況是否會影響FPS的準確度?
後來測試人員分析了系統獲取FPS的原理後,找到了那兩個問題的答案:
Answer:
1)有時候FPS很低,咱們卻感受不到卡頓,由於原本就用不到那麼高的FPS,好比畫一個動畫只畫了0.5秒就畫完了,那麼FPS最高也只有30幀/秒(標準是60幀/每秒),但這並不表明它是卡頓的,用0.5秒動畫就畫完了,不能爲了湊夠60幀/秒,在作個1s的動畫吧。而若是屏幕根本沒有繪製需求,即屏幕顯示的畫面是靜止的,那FPS就爲0。
2)App中止操做後FPS還一直變化,是由於屏幕每一幀的合成都是針對手機裏的全部進程,那麼即便你的App中止了繪製,手機裏其餘進程可能還在繪製,好比通知欄的各類消息,這會致使FPS繼續變化。
從上一節的原理分析來看,對流暢度的評價沒有一個既定的測量標準。不一樣的應用有相對適應的計算方式,總結以下:
系統合成幀率(FPS):數據形式最爲直觀(FPS 是最先的顯示性能指標,並且在多個平臺中都有着相似的定義),且對系統平臺的要求最低(API level 1),遊戲、視頻等連續繪製的應用能夠考慮選用,但不適用於絕大多數非連續繪製的應用;
流暢度(SM):數據形式與 FPS 相似,能夠很好的彌補 FPS 沒法準確刻畫非連續繪製的應用顯示性能的缺陷;
應用跳幀次數、幅度(Aggregate frame stats):除了對系統平臺有較高的要求之外,其採集方式最爲簡單(系統自帶功能);
丟幀(Skipped frames):與 Aggregate frame stats 相似, 信息量相對較少,但可適用範圍更廣
APP須要儘量的超過24幀/秒,接近60幀/秒的速度,而且在使用的過程當中保持這個速率,所以這意味着咱們的程序須要在16.67ms內處理一幅畫面內的全部事,並保持住這個狀態。
計算公式:1000ms / 60 frames ≈ 16.67 ms/frames
操做方法:經過 [設置]->[開發者選項]->[GPU呈現模式分析] ->[在屏幕上顯示爲條形圖] 進行直觀的取樣,截圖以下:
操做:設備鏈接usb數據線,使用adb調試工具,隨後對返回的數據進行適當處理即可以獲得此時此刻app的fps。adb shell dumpsys gfxinfo yourpackagename
解讀:
Draw:是消耗在構建java顯示列表DisplayList的時間。說白了就是執行每個View的onDraw方法,建立或者更新每個View的DisplayList對象的時間。
Process:表示是消耗在Android的2D渲染器執行顯示列表的時間,view越多,要執行的繪圖命令就越多,時間就越長
Execute:消耗在排列每一個發送過來的幀的順序的時間.或者說是CPU告訴GPU渲染一幀的時間,這是一個阻塞調用,由於CPU會一直等待GPU發出接到命令的回覆。因此這個時間,通常都很短。
Draw + Prepare+Process + Execute = 完整顯示一幀 ,這個時間要小於16ms才能保存每秒60幀。
將數據複製到excel中,而後將數據生成「堆積柱形圖」以下:
原理:在 Android 系統中,SurfaceFlinger 扮演了系統中全部 Surface 的管理者的角色,當應用程序所對應的 Surface 更新以後,絕大多數的 Surface 都將在 SurfaceFlinger 之中完成了合併的工做以後,最終纔會在 Screen 上顯示出來。
知道android繪製原理的人應該能明白,SurfaceFlinger就是負責繪製Android應用程序UI的服務,因此surfaceFlinger能反應出總體繪製狀況,通常正常狀況都是連續的,若是出現空檔,一種是沒有操做或者滑動到頭,沒東西須要繪製,這種屬於正常,另外一種就是有問題存在,有其餘操做時間過長。
操做:設備鏈接usb數據線,使用adb調試工具,adb shell dumpsys SurfaceFlinger packagename
首先須要說明的是 Aggregate frame stats 不是一個指標,而是一系列指標集合。咱們來看一個具體的 Aggregate frame stats 的例子:
Stats since: 752958278148ns
Total frames rendered: 82189
Janky frames: 35335 (42.99%)
90th percentile: 34ms
95th percentile: 42ms
99th percentile: 69ms
Number Missed Vsync: 4706
Number High input latency: 142
Number Slow UI thread: 17270
Number Slow bitmap uploads: 1542
Number Slow draw: 23342
以上統計信息的實現能夠詳見源碼:GfxMonitorImpl.java
在 Android M 以上的系統上,上述信息的獲取十分方便(事實上也只有這些系統可以獲取這些信息)。僅須要執行如下命令便可:
adb shell dumpsys gfxinfo <PACKAGE_NAME>
優勢:除了對系統平臺有較高的要求之外,其採集方式最爲簡單(系統自帶功能);
在一次Loop時若是執行時間超過了16.6ms,那麼用多於16.6ms的時間除以16.6ms,便是當前App的丟幀(SF: Skipped Frame)
在16.6ms完成工做卻因各類緣由沒作完,佔了後n個16.6ms的時間,至關於丟了n幀
故:
SF=處理幀數 / (處理幀數 + 額外的垂直同步脈衝) * 60 計算(其中處理幀數常爲128)
這個指標的就是指當前應用在丟幀發生時的丟幀幀數。
針對 Logcat 方案, 該數值直接在 Logcat 中輸出,而且帶有時間信息。
04-18 16:31:24.957 I/Choreographer(24164): Skipped 4 frames! The application may be doing too much work on its main thread. 04-18 16:31:25.009 I/Choreographer(24164): Skipped 2 frames! The application may be doing too much work on its main thread.
針對 Choreographer.FrameCallback 方案 以及 代碼注入方案,咱們可能很方便的經過計算先後兩幀開始渲染的時間差得到這一數值,一樣方便。一樣與 Logcat 方案 不一樣的是,它也是能夠設計成實時計算的。
缺點:Android4.2+系統,適用於SW/HW Rendering 及 部分 OpenGL Rendering
原理:VSync 機制就像一臺轉速固定的發動機(60轉/s),每一轉帶動着作一些 UI 相關的事情。有時候由於各類阻力, 某一圈的工做量比較重, 超過了 16.6ms, 那麼這一秒內就不是 60 轉了。
咱們經過測量這個轉速,來評判應用的流暢度。
和丟幀相對,在VSync機制中1s內Loop運行的次數。和丟幀相對1s內有60個Loop由於某幾回工做時間超過了16.6ms(丟幀),這樣Loop就沒法運行60次(理論最大值)。當流暢度越小的時候說明當前程序越卡頓。
計算方式:SM = 幀率(60) * (單位時長總幀數 - 單位時長丟幀數) / 單位時長總幀數
操做:
VSync機制客戶經過其Loop來了解當前App最高繪製能力,其機制以下:
1)固定每隔16.6ms執行一次;
2)若是沒有繪製事件的時候也會運行這樣一個Loop;
3)Loop在1s以內運行了多少次,便可以表示當前App繪製的最高能力,也就是App卡頓的程度。
if(存在幀的繪製):
Loop = 1 幀繪製完成所佔用的Vsync間隔
else: Loop = 1個Vsync間隔
因此SM計算方法爲Loop在1s內運行了多少次(Loops per seconds),那麼咱們能夠直接在App代碼中經過Choreographer的回調FrameCallback來計算Loop被運行了幾回,從而知道應用的流暢度。但在實際狀況下咱們不必定能修改代碼(實際發佈的版本不容許加入測試代碼)或者根本拿不到代碼(譬如和競品進行對比)。
因此介紹一種更簡單直觀測量Android應用流暢度的方法,就是經過開源測試工具GT(http://gt.qq.com)。
優勢:數據形式與 FPS 相似,能夠很好的彌補 FPS 沒法準確刻畫非連續繪製的應用顯示性能的缺陷;
缺點:Android4.2+系統,適用於SW/HW Rendering 及 部分 OpenGL Rendering
分析UI卡頓咱們通常都藉助工具,經過工具通常均可以直觀的分析出問題緣由,從而反推尋求優化方案,具體以下細說各類強大的工具
咱們能夠經過SDK提供的工具HierarchyViewer來進行UI佈局複雜程度及冗餘等分析
經過命令啓動HierarchyViewer
Hierarchyviewer
接下來Hierarchy window窗口打開:
一個Activity的View樹,經過這個樹能夠分析出View嵌套的冗餘層級,以及每一個View在繪製的使用時長也有表示。
冗餘資源及邏輯等也可能會致使加載和執行緩慢,這可使用Link工具,發現代碼中的流暢度性能問題;
在AndroidStudio 1.4版本中使用Lint最簡單的辦法:就是將鼠標放在代碼區點擊右鍵->Analyze->Inspect Code–>界面選擇你要檢測的模塊->點擊確認開始檢測,等待一下後會發現以下結果:
若是存在冗餘的UI層級嵌套,會進行高亮顯示, 咱們根據提示能夠點擊跳進去進行優化處理掉的。
因爲Android系統會依據內存中不一樣的內存數據類型分別執行不一樣的GC操做,常見應用開發中致使GC頻繁執行的緣由主要多是由於短期內有大量頻繁的對象建立與釋放操做,也就是俗稱的內存抖動現象,或者短期內已經存在大量內存暫用介於閾值邊緣,接着每當有新對象建立時都會致使超越閾值觸發GC操做
根據內存抖動現象,查看log日誌進行分析:
若是看到,這種不停的大面積打印GC致使全部線程暫停的操做一定會致使UI視覺的卡頓,因此咱們要避免此類問題的出現,具體的常見優化方式以下:
檢查代碼,儘可能避免有些頻繁觸發的邏輯方法中存在大量對象分配;
儘可能避免在屢次for循環中頻繁分配對象;
避免在自定義View的onDraw()方法中執行復雜的操做及建立對象(譬如Paint的實例化操做不要寫在onDraw()方法中等);
對於併發下載等相似邏輯的實現儘可能避免屢次建立線程對象,而是交給線程池處理。
有了上面說明GC致使的性能後咱們就該定位分析問題了,咱們能夠經過運行DDMS->Allocation Tracker標籤打開一個新窗口,而後點擊Start Tracing按鈕,接着運行你想分析的代碼,運行完畢後點擊GetAllocations按鈕就可以看見一個已分配對象的列表,以下:
能夠記錄和分析APP每一幀的繪製過程,以及列出全部乃至的OpenGL ES 的繪製函數和耗時;該工具操做後會生成一份記錄App繪製過程和gltrace文件。
在複雜的項目環境中,因爲歷史代碼龐大,業務複雜,包含各類第三方庫,因此在出現了卡頓的時候,咱們很難定位究竟是哪裏出現了問題,即使知道是哪個Activity/Fragment,也仍然須要進去裏面一行一行看,動輒數千行的類再加上跳來跳去調來調去的,結果就是不了了之隨它去了,實在不行了再優化吧。因而一拖再拖,最後可能壓根就改不動了,客戶端愈來愈卡。
Android應用卡頓是很是廣泛的現象,偶爾出現ANR。只有當APP出現ANR,咱們才能獲得當前堆棧信息。當應用只是卡頓或只是不太流暢的時候,咱們能不能找出卡頓元兇呢?不依賴Debug和源碼的狀況,能不能找出卡頓的堆棧信息呢?咱們須要找到一種方法來檢測哪些函數可能會使應用發生ANR,在開發階段就能找出卡頓元兇,提升應用流暢度。
BlockCanary就是來解決這個問題的。告別打點,告別Debug,哪裏卡頓,一目瞭然,讓優化代碼變得有的放矢。
具體使用方法請點擊:
這次質量開放平臺-評測中心(http://fit-stg1.jryzt.com/Hyperion-server/html/index.html)的性能測試的流暢度測試主要是針對場景頁面的掉幀率數據採集進行對比分析, 原理公式爲:掉幀率=處理幀數 / (處理幀數 + 額外的垂直同步脈衝) * 60 計算(其中處理幀數常爲128)。通常掉幀率超過10%,咱們就認爲存在卡頓有必要進行分析定位。
這裏選取了同一家銀行的兩個APP與行業競品進行掉幀率對比分析,從掉幀率對比看,行業競品均值爲4.1%,90分位約13.1%,75分位約27.5%,中位數約39.6%,25分位約59.9%。
【福建農信】掉幀率爲1.783%,表現良好,戰勝了行業90%以上的競品
【榕商Bank】掉幀率爲6.244%,表現良好,戰勝了行業90%以上的競品
總體得分對比分析:
從首頁啓動到加載完成場景分析,【福建農信】實際啓動到首頁場景只有一個簡單的未登陸頁,相比於豐富多樣的【榕商Bank】來講屬於很是簡單的頁面,可是它的掉幀率與豐富資源的【榕商Bank】比較相差不遠。
【福建農信】首頁掉幀率問題分析:
單純從頁面表象觀察,【福建農信】啓動時,未登陸頁是從APP背景頁下方飄進漸漸上升在頁面中間,而後抖動一下再靜止,有一種PPT飛入的動態效果。
經過深刻分析得出【福建農信】應用交互中主線程存在卡頓,存在 Activity(LoginActivity)切換過慢的現象:cn.com.fjnx.mobilebank.per.activity.account.LoginActivity.onCreate(阻塞1639 ms)
com.yitong.fjnx.mbank.android.Splash.onCreate(阻塞1717 ms)
建議【福建農信】優化啓動時未登陸頁進入的方式
【榕商Bank】首頁掉幀率問題分析:
首頁加載掉幀率爲8.2%,經過GPU過分繪製調試發現:com.pingan.fstandard.activity.MainActivity存在過分繪製。其實是由於運營Banner位有輪播動態效果,輪播間隔時間設置的比較長,致使評測時掉幀率偏高,可是這是合理的產品設計,並且也不影響用戶體驗。
綜上對比,【榕商Bank】流暢度表現優於【福建農信】,【福建農信】掉幀率仍然有優化空間。
問題:UI線程中有I/O讀寫、數據庫訪問等耗時操做,致使UI線程卡頓;
定位及解決:TraceView 尋找卡住主線程的地方,Systrace 獲取 app 運行是線程的信息以及 API 的執行狀況,避免在主線程執行 IO 操做。
問題:不合理的佈局雖然能夠完成功能,但隨着控件數量越多、佈局嵌套層次越深,展開佈局花費的時間幾乎是線性增加,性能也就越差;
定位及解決: 避免OverDraw致使的性能損耗;能夠參考《Android性能優化(二)之佈局優化面面觀》
問題:同一時間動畫執行的次數過多,致使CPU或GPU負載太重;
問題:內存抖動、內存泄漏都會致使:GC的次數越多、消耗在GC上的時間越長,CPU花在界面繪製上的時間相應越短;
解決:節省內存的分配空間,儘量的下降GC的頻率,縮短GC的平均時間;CPU不被佔用,卡頓的概率就會更低; 能夠參考《Android性能優化(四)以內存優化實戰》
問題:對線程開啓方式的不一樣選擇以及不一樣配置均可能致使卡頓的發生;
解決:任何耗時操做正確的移到異步裏,類如I/O讀寫、數據庫訪問等都應該採用異步的方式,不能有「只是一個很小的文件」之類的想法,防微杜漸;
參考:
Android 性能模式 第一季
Android性能優化典範 - 第1季
Android性能優化之渲染篇
Android性能優化系列——Profile GPU Rendering
Profile GPU Rendering Walkthrough
Android 顯示原理簡介
Android 4.4 Graphic系統詳解(2) VSYNC的生成
理解 VSync
瞭解Android 4.1,之三:黃油項目 —— 運做機理及新鮮玩意
Hierarchy Viewer使用詳解
插播:金融壹帳通質量開放平臺現提供測試一站式解決方案,包括UI自動化、測試過程管理、app評測、接口自動化、接口壓測、輿情監控等測試服務,歡迎訪問:http://fit-stg1.jryzt.com/Hyperion-server/html/index.html。