Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發 >> 關於android開發 >> Android APP性能分析方法及工具,androidapp

Android APP性能分析方法及工具,androidapp

編輯:關於android開發

Android APP性能分析方法及工具,androidapp


近期讀到《Speed up your app》一文。這是一篇關於Android APP性能分析、優化的文章。在這篇文章中,作者介紹他的APP分析優化規則、使用的工具和方法。我覺得值得大家借鑒。英文好的讀者可讀原文(鏈接:http://blog.udinic.com/2015/09/15/speed-up-your-app)。

1、作者的規則

作者每次著手處理或尋找性能問題時,遵循下列規則:

  • 時常檢測

在更新APP前後,用測試工具軟件多檢測幾次APP性能,可快速得到測試數據。這些數字是不會說謊的。而僅僅用眼睛觀察APP性能是肯定不行的。如你在觀察幾次相同的動畫效果後,你就想象它運行的夠快了,從而忽略一下問題。

  • 使用低端設備

如今硬件性能在不斷的提升,如果僅在最新設備運行APP,可能不能充分暴露出APP中存在的性能問題。另外,盡管用戶設備換手率已經很高了,但仍然不是所有用戶都是使用最新的和最強功能的設備。所以應該使用低端設備上,運行APP,這可以幫助你更有效地發現問題。

  • 權衡

性能優化是要綜合各方面因素進行評判、權衡。因為優化一項性能可能是要以犧牲另一項性能為代價的。分析、查找、修復是要花費很多時間。你要准備好自我犧牲。

2、作者的分析方法及使用工具

作者采用自頂向下方法,從手機運行的概況開始,逐級深入分析:方法的性能、內存使用情況、GPU渲染效果、視圖層次、圖形疊加繪制、圖像透明值;解釋Honeycomb引入的硬件加速以及視圖層。

2.1 Systrace

手機實際就是一台功能強大的計算機,同時間做很多事情。Systrace能夠展示手機運行的概況。

作者在Systrace中選取一個Alert,做為例子,講解分析發現問題的方法:

1)由Alert找到函數(如:long View#draw()),再展開“Inflation during ListView recycling”

2)可以查看到函數的耗時,更詳細觀察分析其中哪項花費時間較長。

3)選擇一幀查看它花費多久時間。如有一幀繪制用時超過19ms。展開“Scheduling delay”

4)它的值(Wall duration和CPU duration之間的差異)表明有很長時間CPU沒有安排這個線程。

這就需要查查在整個這段時間裡CPU都做了什麼?但是Systrace只能查看運行概況,還不能得到更深層次的分析。為了找到CPU運行繁忙的真正原因,作者使用另一個工具:Traceview。

2.2 Traceview

Traceview是性能分析工具,可以顯示每個方法運行時間。可從Android Device Monitor中啟動,也可從代碼中啟動。

作者以“滾屏”動作為例說明Traceview分析方法。在“滾屏”動作的跟蹤記錄中,找到getView()方法,發現它被調用12次,每次CPU用時約3ms。但是每次完成整個調用卻用時162ms!這就是個問題!

作者就繼續查看getView()的子方法,查看各個子方法所用時間在總時間中比例。他發現Thread.join()的Inclusive Real Time占用約98%。他順籐摸瓜,找啟動子方法的Thread.run()方法(它是創建一個新線程時所調用的方法),逐個跟著,直到找到“元凶”:BgService.doWork()方法。

另外,GC(Garbage Collector – 垃圾收集器)不定期運行清理不用的對象。GC的頻繁運行也會使得APP運行慢下來。為此,作者的下一步就是針對內存進行分析。

2.3 內存分析(Memory Profiling)

Android Studio逐步在改善,有越來越多的工具可以幫助我們找到和分析性能問題。作者用其分析內存使用情況。

2.3.1 Heap dump

Heap Dump可以看到Heap中依據類名排序實例的直方圖。每個都有分配對象的總數,實例的大小和留在內存中對象的大小。後者告訴我們這些實例釋放後,能夠釋放多少內存。這可幫助我們識別出大的數據結構和對象關系。這些信息可以幫助我們構建更有效的數據結構,解開對象之間聯系以減少內存駐留,最終盡可能的減少內存占用。

在分析中,可發現“內存洩漏”。解決方法就是要記得在活動即將被銷毀時調用onDestory()方法刪除引用。

內存洩漏和較大對象的heap空間占用,使得有效內存減少,頻繁引發GC事件,試圖釋放更多的heap空間。這些GC事件占用CPU,降低了APP性能。如果有效的內存數量不足與滿足APP,且heap空間不能在擴大,就引發 —— OutOfMemortException —— APP崩潰。

Eclipse MAT(Eclipse Memory Analyze Tool)也是不錯的內存分析工具。

2.3.2 Allocation Tracker

Allocation Tracker可生成在跟蹤期間內存分配給所有實例的情況報告,報告可按類分組或按方法分組。它以很好的可視化方式展示哪個實例所獲得內存最多。

使用這些信息,可以找出分配大內存的方法,和可能頻繁觸發GC的事件。

2.3.3 General memory tips

作者給出一些技巧:

  • 枚舉

一直是討論性能的熱門話題。枚舉比普通常數占用更多的內存空間嗎?是的。但這肯定是壞事嗎?未必。如在編寫代碼庫,需要強類型安全性,這就應該使用它。如果有一組可以歸結在一起的常數,此時使用枚舉也許不不合適。怎樣決定,你需要權衡考慮。

  • Auto-boxing

是自動將原始數據類型轉換為其對應的對象表示(如:int 到 Integer)。每次原始數據類型被“裝箱”到對象,就會產生一個新的對象(我知道這令人震驚)。如果有許多這樣的操作,那麼GC就頻繁地運行。由於將原始類型數據賦值到對象時,自動進行auto-boxing的,就很容易忽視它的數量。解決方案就是盡量使數據類型保持一致。如果要在整個APP中使用原始數據類型,就盡量避免在沒有實際需要時進行Auto-boxing。使用內存分析工具可以找到許多對象是表示原始數據類型。也可以用Traceview尋找到Integer.valueOf(),Long.valueOf()等等。

  • HashMap與ArrayMap / Sparse*Array

在Auto-boxing相關問題中,使用HashMap時,就要求用對象作為鍵值。如果在APP中用原始int類型,那麼在使用HashMap時就需要將int轉化到Integer。這種情況也許就需要用SparesIntArray。如果在鍵值仍然需要對象類型的情況下,也可以改用ArrayMap類。它非常類似HashMap,只是在其內部工作方式不同,是以降低速度為代價,使用較少的內存。這兩者占用內存都比HashMap小,但檢索所花費的時間略高於HashMap。如果數據項少於1000,它們的運行時沒有什麼差別。

  • Context Awareness

Activity內存比較容易洩漏。由於它們保持UI的所有視圖層次,占用大量的空間,所以它們的洩漏也是非常“昂貴的”。許多操作都要求Context對象,你發起Activity。假如引用被緩存,並且該對象的存活期要長於你的Activity,如果沒有清理它的引用,你就產生了內存洩漏。

  • 避免非靜態內部類(inner class)

創建一個非靜態內部類,並實例化它,就創建對外部類的隱式引用。如果內部類實例需要的時間比外部類長,這外部類就要在內存中保留,即使它不再需要了。例如,在Activity類內部,創建一個擴展AsyncTask的非靜態類,然後著手啟動異步任務,在它運行時,銷毀活動。該異步任務在其運行期間,都保持這一Activity運行。解決方案就是不要這樣做。如果需要這樣,就聲明一個靜態內部類。

2.4 GPU Profiling

Android Studio 1.4增加一項新功能:分析GPU渲染功能。作者詳細講解這一新功能的分析方法。

在GPU選項卡下,可以在屏幕上看到圖形化顯示的渲染每幀所花費的時間。圖形中每條都表示被渲染的一幀。顏色表示進程的不同周期:

  • 繪畫(藍色)

表示View#onDraw()方法。那部分建立/更改DisplayList對象,然後轉換成GPU能夠理解的OpenGL命令。高的條形可能是視圖復雜,而要求更多的時間繪制它們的顯示列表,而許多視圖在短時間內就失效了。

  • 准備(紫色)

在Lollipop中,加入另一個線程,以幫助UI線程渲染更快。這個線程叫:RenderThread。它的責任是轉換顯示列表為OpenGL命令,再發送給GPU。這樣在渲染過程中,UI線程可以開始處理下一個幀。這時UI線程將所有資源傳送給RenderThread。如果有許多資源要傳遞(如許多/繁重顯示列表),這一步可能需要較長時間。

  • 處理(紅色)

執行顯示列表產生OpenGL命令。由於需要視圖重繪,如果有許多/復雜顯示列表要執行轉換,這一步可能需要較長時間。當視圖無效或是移動時,都要要重繪視圖。

  • 執行(黃色)

發送OpenGL命令到GPU。由於CPU發送這些緩存的命令到GPU,並期待收回干淨緩存,這就阻塞調用了。緩存數量有限,並且GPU也很忙 —— CPU會發現自己必須先等待緩存釋放。因此,如果在這一步我們見高的條形,就可能意味著GPU在繪制UI時非常忙,這個繪制在短時間內太復雜了。

具體操作實例見原文。

2.5 Hierarchy Viewer

作者喜愛這個工具。他對許多開發者根本不使用這工具感到失望。

使用Hierarchy Viewer,可以完整地觀察到屏幕視圖層次和所有視圖的屬性。還可以導出主題(theme)數據,查看到每個樣式的所有屬性。但是,這只是在Hierarchy Viewer獨立運行時,才能查看這些數據。而不可以從Android監控器中查看。

作者在設計布局和優化布局時使用這個工具。

作者認為有時間,可以對每張視圖都測量以及它的所有子視圖。顏色表示視圖與樹中其他視圖的比較情況,很容易找出最薄弱的環節。由於我們可以預覽視圖,這樣就可通過視圖樹,跟蹤找出可刪除的冗余步驟。這其中,對性能影響最大的,被稱為Overdraw。

2.6 Overdraw

如果GPU需要在屏幕上繪制很多內容,繪制每幀都需要增加時間,這樣執行周期就拉長了,在圖形中以黃色表示。在一些圖形上再疊加繪制,如在紅色背景上繪制黃色按鈕,這就發生Overdraw。這種情況下,GPU需要先繪制紅色背景,再在其上繪制黃色按鈕,Overdraw就不可避免了。如果有太多的Overdraw層,這就使得GPU超負荷運行,偏離16ms的目標。

設置“Debug GPU Overdraw”開發者選項,所有Overdraw的嚴重程度都以顏色表示出來。1~2倍的Overdraw算好的,甚至有些小的紅色區也不壞。但是如果在屏幕上有許多紅色,這就有問題了。但是都被紅色覆蓋。這就是問題了。作者建議這時僅用一種顏色設置背景來解決這個問題。

注意:默認主題聲明一個全屏窗口背景顏色。如果有不透明布局的Activity覆蓋在整個屏幕上,可以通過刪除窗口的背景色消除這層Overdraw。

Hierarchy Viewer能夠輸出所有層次到PSD文件中,用Photoshop中打開。在Photoshop中研究不同的層就可展示布局中的所有Overdraw。刪除冗余的Overdraw,努力性能提高到藍色上。

2.7 Alpha

使用透明效果也會影響性能。為什麼?

ImageView相互重疊。用setAlpha()設置alpha值,這將傳遞給所有的子視圖,對幀緩沖區進行繪制。結果都重疊混在一起。幸好,OS有這個問題的解決方案。布局數據被復制到off-screen緩沖區,用alpha值對off-screen緩沖區進行處理後,再復制到幀緩沖區中。效果就好了些。但是,我們為此付出了代價:把“幀”緩沖區改為off-screen緩沖區,實際上增加了一個隱含的Overdraw層。OS就不知道處理了,所以默認情況下經常要進行復雜地操作。

不過還是有方法設置alpha值,避免off-screen緩沖區增加的復雜性:

  • TextView

用setTextColor()替代setAlpha()。使用文本顏色的alpha通道,就可直接用它來繪制的文本。

  • ImageView

用setImageAlpha()替代setAlpha()。理由同TextView。

  • Custom View

如果自定義視圖不支持覆蓋視圖,這復雜行為是無關緊要的。可通過重寫hasOverlappingRendering()方法,讓其返回false,通知OS直接繪制自定義的視圖。還可以通過重寫onSetAlpha()方面,讓其返回true,選擇手動處理設置,各alpha值對應的操作。

2.8 Hardware Acceleration

在Honeycomb(蜂巢,Android 3.x)引入硬件加速後,在屏幕上渲染APP可以以新的繪制模型(http://developer.android.com/guide/topics/graphics/hardware-accel.html)進行。新模型引入DisplayList結構,記錄視圖渲染繪制命令。還有另一個很好的特性,時常被開發人員忽視或不正確地使用 — 視圖層。

使用視圖層,我們能夠非屏幕緩沖區(如前面所見,應用alpha通道)渲染視圖,並且能按照我們的意願操控它。由於利用這一特性能夠更快地繪制復雜動畫視圖,所以它主要用於動畫。沒有這些層次,在改變動畫屬性(如:x坐標、縮放、alpha值等等)後,動畫視圖將無效。對於復雜視圖,這個無效效果都傳遞到所有子視圖,且重繪的成本很高。在硬件支持下,使用視圖層時,GPU會為視圖創建紋理。有幾個操作可以用於紋理,而不會破壞它,如:X / Y位置、旋轉、alpha等等。所有都意味著在動畫期間,可以在屏幕上繪制復雜動畫視圖,而完全不會破壞它。這使得動畫更加順暢。

提出在使用硬件層時需要記住幾件事:

  • 清理視圖。硬件層消耗有限存儲元件的空間、GPU。所以僅在確實需要的時候使用(像動畫),並在事後清理。
  • 如果在使用硬件層後,改變視圖,硬件層無效,並在非屏幕緩沖區重繪視圖。這會發生在改變那些無硬件層優化的屬性上(迄今為止,僅優化:旋轉、縮放、x/y、轉換、軸移和alpha)。

3、其他資料

作者為說明性能分析、優化,准備很多代碼來模擬情景。這些代碼可以在Github代碼庫(https://github.com/Udinic/PerformanceDemo) 或是 Google Play(https://play.google.com/store/apps/details?id=com.udinic.perfdemo) 找到。他將不同的場景分別放到不同的Activity中,並它們編寫文檔,盡可能幫助理解使用這些Activity會遇到哪方面的問題。可以用工具和運行APP來閱讀Activity的javadoc。

 

作者還推薦一些學習交流方法:

    • 學習Android圖形架構(http://source.android.com/devices/graphics/architecture.html)是怎樣工作的。這裡有你需要知道的Android怎樣渲染UI的一切,講解不同的系統元素,如:SurfaceFlinger,以及它們之間怎樣相互交互的。這篇文檔很長,但是值得一讀。
    • 關於Google IO 2012談話(https://www.youtube.com/watch?v=Q8m9sHdyXnE),展示繪圖模型的工作原理,和如何/為什麼在UI渲染時會有一個Jank。
    • Android性能研討會(https://www.parleys.com/tutorial/part-2-android-performance-workshop), 從Devoxx 2013開始,展示Android 4.4中繪圖模型的一些優化,演示不同的性能優化工具(Systrace,Overdraw等等)。
    • 關於預防性優化(https://medium.com/google-developers/the-truth-about-preventative-optimizations-ccebadfd3eb5)的長篇文章,說明這為什麼不同於提早(premature)優化。由於許多開發者認為影響不明顯,所以沒有優化他們的部分代碼。要記住一點,積少成多就是一個大問題了。如果你有機會優化一小部分代碼,那怕它可能微乎其微,也不要放棄優化。
    • Android的內存管理(https://www.youtube.com/watch?v=_CruQY55HOk)這是Google IO 2011的舊視頻。它仍然很有意義。它展示了Android怎樣管理APP的內存的,以及怎樣用工具(如Eclipse MAT)找出問題。
    • Google工程師Romain Guy所做的案例分析(http://www.curious-creature.com/docs/android-performance-case-study-1.html),怎樣優化Twitter客戶端。在這個案例分析中,Romain展示了他怎樣在APP中找到性能問題,以及他建議如何修改它們。在一篇回帖中,展示了在重新設計同一APP後產生的其他問題。

作者希望大家已獲得足夠資料和更強的自信。從今天開始優化自己的APP!

 

作者關於性能優化的演講視頻在這裡:http://www.youtube.com/embed/v3DlGOQAIbw?color=white&theme=light

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved