Android APP性能分析工具

本文基本翻譯自Facebook工程師的文章
Speed up your app型豁,也加入了自己的一些內(nèi)容。

會(huì)介紹以下幾個(gè)主題

  • Systrace
  • Traceview
  • Memory Profiling
  • Allocation Tracker
  • GPU Profiling
  • Hierarchy Viewer
  • Overdraw
  • Alpha
  • Hardware Acceleration

Systrace

Systrace的功能可以在AS的DDMS中找到笆制,但不太穩(wěn)定,所以這里只介紹命令行模式赖条。
另外Systrace只能分析概況如孝,不能定位問題位置宪哩,不太感興趣的朋友也可以直接跳到第二節(jié)Traceview。
在終端(我的環(huán)境是MAC)中先進(jìn)入sdk/platform-tools/systrace目錄
cd /Users/apple/Documents/Android/sdk/platform-tools/systrace

然后執(zhí)行Systrace的命令是
python systrace.py --time=10 -a com.duotin.fm -o mynewtrace.html sched gfx view wm
--time=10 表示記錄10秒
更多參數(shù)說明請(qǐng)查看官網(wǎng)
執(zhí)行完打開生成的mynewtrace.html文件
呈現(xiàn)這樣的界面

這里寫圖片描述

點(diǎn)擊第一列的三角形警示圖標(biāo)或者第二列的圓形警示圖標(biāo)第晰,都可以查看警示詳情锁孟,點(diǎn)擊效果分別如下

這里寫圖片描述

三角形警示圖標(biāo)彬祖,每個(gè)圖標(biāo)代表一個(gè)警示,點(diǎn)擊后查看警示詳情品抽。

這里寫圖片描述

圓形警示圖標(biāo)储笑,每個(gè)圖標(biāo)代表一個(gè)frame,顯示紅色或者黃色表示此frame的時(shí)間已經(jīng)超過16.6 millisecond per frame的標(biāo)準(zhǔn)圆恤,會(huì)導(dǎo)致界面失幀突倍。點(diǎn)擊后查看這個(gè)frame所有的警示。
此時(shí)按"M"快捷鍵可以高亮當(dāng)前選中的frame盆昙∮鹄“A"和"D"分別為左移和右移視圖,"W"放大淡喜,"S"縮小秕磷。
此外,點(diǎn)擊各顏色塊炼团,可以查看各顏色塊的詳情澎嚣。
點(diǎn)擊上圖中的第一個(gè)Alert: Inflation during ListView recycling
顯示詳情

這里寫圖片描述

可以看出Inflation during ListView recycling的執(zhí)行時(shí)間是32ms(遠(yuǎn)遠(yuǎn)超過了16ms的限制),共5個(gè)item,平均到每個(gè)item為6ms瘟芝。通過點(diǎn)擊該frame范圍內(nèi)的顏色塊易桃,可以查看各個(gè)方法的詳情。

我們?cè)冱c(diǎn)擊一個(gè)圓形警示圖標(biāo)并高亮

這里寫圖片描述

頂部顯示此frame耗時(shí)19ms, 點(diǎn)擊右下方的Alert锌俱,顯示有 Scheduled delay晤郑。
Scheduled delay 是指告訴CPU執(zhí)行任務(wù),但CPU太忙了嚼鹉,任務(wù)被延遲執(zhí)行了贩汉。
點(diǎn)擊下面的一個(gè)顏色塊,顯示如下

這里寫圖片描述

Wall duration 是指此顏色塊代表的方法從開始到結(jié)束的耗時(shí)
CPU duration 是指 CPU的執(zhí)行時(shí)間
可以發(fā)現(xiàn)CPU duration只有4ms锚赤,但Wall duration有18ms。
延遲這么嚴(yán)重褐鸥,我們來看看原因线脚。
在選中的顏色塊上方,我們看到四個(gè)CPU都被顏色塊填充叫榕,表示此時(shí)4個(gè)CPU都有活干浑侥,很忙。
我們選中一個(gè)CPU的顏色塊

這里寫圖片描述

可以發(fā)現(xiàn)占用CPU的應(yīng)用是com.udinic.keepbusyapp
恩晰绎,對(duì)Systrace的介紹到此結(jié)束了寓落,雖然還有些沒講,但Systrace的確只能看個(gè)概覽荞下。
且慢伶选,再加送一個(gè)tip, 點(diǎn)擊右邊欄的Alerts, 你能看到所有的Alerts.

這里寫圖片描述

通過這張圖我分析出
inefficient view alpha usage數(shù)量最多史飞。Inflation during ListView recycling影響時(shí)間最長(zhǎng),耗時(shí)長(zhǎng)達(dá)52ms仰税。
inefficient view alpha usage是因?yàn)檎{(diào)用具體View的setAlpha方法,而View的setAlpha在Android中是很昂貴的操作构资。解決方法是用ARGB設(shè)置color代替直接調(diào)用setAlpha;如果是ImageView陨簇,調(diào)用ImageView#setImageAlpha吐绵;如果是自定義View,覆蓋hasOverlappingRendering()或者onSetAlpha()或者通過paint.setAlpha實(shí)現(xiàn)河绽。詳細(xì)請(qǐng)參考文章1己单,文章2, 文章3

Traceview

Traceview能夠在方法層面上分析APP的性能,非常強(qiáng)大耙饰。
可以通過荷鼠,命令行或者GUI啟動(dòng),我用的是GUI啟動(dòng)榔幸,點(diǎn)擊AS的Android Device Monitor, 點(diǎn)擊Devices欄目下面的 Start Method Profilling的圖標(biāo) , 在對(duì)話框中選擇允乐,(我選的是trace Based Profilling,表示實(shí)時(shí)分析,會(huì)比較慢削咆,但分析結(jié)果詳細(xì))牍疏,操作APP, 分析結(jié)束的時(shí)候,點(diǎn)擊同一個(gè)圖標(biāo)即可拨齐。更多操作請(qǐng)?jiān)L問官網(wǎng)
先看下界面

這里寫圖片描述
列名 意義
Name 方法名鳞陨,每個(gè)方法的顏色都不一樣。
Inclusive CPU Time 此方法占用CPU的時(shí)間瞻惋,Inclusive指包括調(diào)用的方法所
Exclusive CPU Time 此方法所占用CPU的時(shí)間厦滤,Exclusive 指不包括調(diào)用的方法
Inclusive / Exclusive Real Time Real Time指方法從開始到結(jié)束消耗的時(shí)間,跟Systrace中的Wall duration一個(gè)意思歼狼。
Calls+Recursion 此方法被調(diào)用了多少次+多少次是遞歸調(diào)用
Calls / Total 子方法被此父方法調(diào)用的次數(shù)/子方法被調(diào)用的總次數(shù)

點(diǎn)擊某條目下的parent 或者 child 方法時(shí)掏导,會(huì)跳到該方法的條目。
想找出最影響性能的方法羽峰,可以點(diǎn)擊Exclusive CPU time一欄趟咆,找出消耗時(shí)間最長(zhǎng)的幾個(gè)方法。如果是應(yīng)用的方法梅屉,直接看可不可優(yōu)此方法值纱。如果是系統(tǒng)方法,通過查看其父方法坯汤,追溯至應(yīng)用方法虐唠。
而查看子方法,可以看出此方法到底做了什么惰聂。
如果要找UI卡頓的原因疆偿,可以從 具體Adaper類#getView 具體View#ondraw, 具體View#onMeasure等方法入手咱筛。

方法執(zhí)行時(shí)占用了CPU,所以執(zhí)行時(shí)間過長(zhǎng)會(huì)造成UI渲染被延遲,從而應(yīng)用不流暢翁脆。而GC同樣會(huì)占用CPU眷蚓,AS也同樣提供了查看GC的工具:

(譯者注:
Android性能分析工具Systrace和TraceView的使用

Memory Profiling

點(diǎn)擊AS中的Android Monitor, 選中Memory | CPU 一欄, 界面如下

這里寫圖片描述

如圖所示,小幅的內(nèi)存下降一般就是發(fā)生了GC反番。
點(diǎn)擊左側(cè)的Heap dump沙热,會(huì)生成內(nèi)存中的所有對(duì)象的快照。

這里寫圖片描述
列名 意義
Total Counts 內(nèi)存中該類的對(duì)象個(gè)數(shù)
Heap Count 在該堆中該類的對(duì)象個(gè)數(shù)罢缸,左上角可以選擇App heap或Zygote heap
Sizeof 單個(gè)對(duì)象占用的Shallow Size
Shallow Size 所有對(duì)象所占用的Shallow Size
Retained Size 所有對(duì)象所占用的Retained Size,即GC后會(huì)釋放的內(nèi)存
instance 該類一個(gè)具體的對(duì)象
Reference Tree 引用這個(gè)對(duì)象的父對(duì)象篙贸,點(diǎn)擊父對(duì)象,展開這些父對(duì)象的父對(duì)象

什么是Shallow Size 和Retained Size

選中一行枫疆,點(diǎn)擊右側(cè)的一個(gè)instance,可以在下方看到Refrence Tree界面

這里寫圖片描述

在圖中可以看出MemoryActivity的一個(gè)instance在ListenerManager中被引用了爵川。如果MemoryActivity已經(jīng)不在Activity棧中了,這樣的引用就是內(nèi)存泄漏息楔。另外一個(gè)檢查內(nèi)存泄漏的工具是leakcanary
通過查看Retained Size和Reference Tree寝贡,我們可以知道哪些對(duì)象占用了較多的內(nèi)存,對(duì)象間的引用關(guān)系值依,進(jìn)而分析是否可以優(yōu)化數(shù)據(jù)結(jié)構(gòu)圃泡,減少引用關(guān)系,以減少內(nèi)存占用和GC頻率愿险。

Allocation Tracker

Memory | CPU一欄左側(cè)的另一個(gè)按鈕Allocation Tracker也是用于分析內(nèi)存占用颇蜡。點(diǎn)擊一次表示開始記錄,再次點(diǎn)擊表示停止記錄辆亏。
在結(jié)果頁面的左上方點(diǎn)擊餅狀圖风秤。
可以選擇 group by Allocator,即按對(duì)象劃分扮叨。

這里寫圖片描述

或者 group by method

這里寫圖片描述

Allocator下面的餅狀圖最外圍的是具體的類缤弦,內(nèi)部的是包名。圖中可以看出包或者對(duì)象占用的內(nèi)存大小或者個(gè)數(shù)甫匹,面積越大甸鸟,占用或者個(gè)數(shù)越多。選擇size可以查看占用內(nèi)存最多的對(duì)象兵迅,選擇count可以查看以及個(gè)數(shù)最多的對(duì)象。前者我們可以試著優(yōu)化類薪贫,后者我們可以嘗試建立一個(gè)Object pool來復(fù)用對(duì)象恍箭。

從group by method可以看出,decode方法占用的總內(nèi)存達(dá)10.91M, 就有可能是方法內(nèi)新建了太多對(duì)象瞧省,可以往這方面優(yōu)化扯夭。

內(nèi)存方面的tips:

1. Enums

Enums比int占的內(nèi)存大得多鳍贾,而且有替換方案@IntDef, 所以除了某些情況,比如你需要強(qiáng)制指定類型交洗,不然的話int會(huì)更節(jié)省內(nèi)存

2. 自動(dòng)裝箱

自動(dòng)裝箱指從基本類型自動(dòng)轉(zhuǎn)換到對(duì)象形式的(比如int到Integer)骑科,鑒于基本類型使用的場(chǎng)景和次數(shù)都較多,所以需要盡量避免使用其自動(dòng)裝箱的形式构拳。

3. HashMap vs ArrayMap / Sparse*Array

如果我們需要使用int作為Map的value咆爽,可以使用SparseIntArray,比起使用HashMap對(duì)int自動(dòng)裝箱置森,要省內(nèi)存的多斗埂。如果要使用Object作為Map的key,除了HashMap凫海,你也可以考慮使用ArrayMap,功能和HashMap一樣呛凶,但更省內(nèi)存,點(diǎn)擊了解原理行贪。盡管時(shí)間性能上HashMap更勝一籌漾稀,但除非你要存儲(chǔ)1000個(gè)以上對(duì)象,否則他們使用起來幾乎一樣快建瘫。

4. 注意Context對(duì)象

因?yàn)镃ontext在開發(fā)中的使用場(chǎng)景較多崭捍,所以最容易造成內(nèi)存泄漏。Activity本身是一個(gè)heavy的對(duì)象暖混,為了避免內(nèi)存泄漏缕贡,可以穿ApplicationContext的話,就不要傳Activity了拣播。

5. 避免非靜態(tài)內(nèi)部類

非靜態(tài)內(nèi)部類隱式持有外部類的引用晾咪,所以如果外部類不再被需要,但內(nèi)部類仍在使用狀態(tài)贮配,就造成了內(nèi)存泄漏谍倦。特別是Activity類,在定義內(nèi)部類的時(shí)候盡量定義成static的泪勒。

GPU Profiling

首先在手機(jī)的開發(fā)者選項(xiàng)頁面昼蛀,點(diǎn)擊GPU呈現(xiàn)模式分析(Profile GPU rendering),選中“In adb shell dumpsys gfxinfo” 然后在AS的Android Monitor 界面選中GPU一欄圆存,確保左上方的暫停按鈕沒有選中叼旋,此時(shí)AS就開始按照選定的包名顯示GPU情況了。每一個(gè)條直線表示UI渲染中的一幀沦辙,不同的顏色表示不同的繪制階段夫植。

這里寫圖片描述
  • Draw(藍(lán)色) 執(zhí)行的是View#onDraw()方法。這個(gè)階段的工作是創(chuàng)建DisplayList對(duì)象油讯,這些對(duì)象稍后將被轉(zhuǎn)換成OpenGL命令详民,傳送到GPU延欠。如果藍(lán)色較長(zhǎng),一般是因?yàn)檩^復(fù)雜的View, 或者短時(shí)間內(nèi)invalidate了較多的View
  • Prepare (紫色) Lollipop才引入的階段沈跨,用于加快UI渲染由捎,在線程RenderThread中執(zhí)行。這個(gè)階段的任務(wù)是將第一步產(chǎn)生的display lists轉(zhuǎn)換成OpenGL命令饿凛,并傳送到GPU狞玛。此時(shí)UI thread將繼續(xù)處理下一幀。UI Thread給RenderThread傳遞所需的資源產(chǎn)生的耗時(shí)也記錄在此階段中笤喳。當(dāng)有大量的資源要傳遞为居,比如很多/很heavy的display lists,這個(gè)階段耗費(fèi)的時(shí)間會(huì)增多杀狡。
  • Process(紅色) 處理display lists蒙畴,產(chǎn)生OpenGL命令,較多或者較復(fù)雜的display lists會(huì)使此階段耗時(shí)增加呜象,因?yàn)楹芏郪iew將被redraw膳凝。View被redraw的情況有invalidate或者之前覆蓋在上面的View現(xiàn)在被移走了。
  • Execute (黃色) 將OpenGL命令發(fā)送給GPU, 這是個(gè)阻塞方法恭陡,因?yàn)镃PU通過buffer將OpenGL命令發(fā)送給GPU, 當(dāng)處理完畢返回空的buffer蹬音。buffer的數(shù)量有限,所以當(dāng)GPU很忙休玩,buffer也用完了著淆,CPU就需要等待GPU處理完返回一個(gè)空的buffer,才能繼續(xù)發(fā)送OpenGL命令。因此如果這個(gè)階段耗時(shí)較多拴疤,一般是因?yàn)樵诶L制復(fù)雜的View永部。

在 Marshmallow 版本,增加了更多的顏色

這里寫圖片描述

根據(jù)谷歌工程師John Reck提供的信息,
圖中的Animation 是指所有通過Choreographer 注冊(cè)的CALLBACK_ANIMATION呐矾,包括
Choreographer#postFrameCallback View#postOnAnimation苔埋。這兩個(gè)函數(shù)在 view.animate(), ObjectAnimator, Transitions等場(chǎng)景中有用到。systrace中的Animation也是這個(gè)意思蜒犯。
misc指的是接收到的vsync的時(shí)間戳和當(dāng)前時(shí)間的延遲组橄。
很多人都看到過Choreographer的log "Missed vsync by。罚随。玉工。ms skipping 。淘菩。瓮栗。 frames",這就是misc瞄勾。換句話說费奸,就是在記錄幀狀態(tài)時(shí)INTENDED_VSYNC和VSYNC的差別
要使用這個(gè)功能,你需要在手機(jī)的開發(fā)者選項(xiàng)中開啟GPU rendering
這里寫圖片描述

此工具原理是ADB命令

adb shell dumpsys gfxinfo <PACKAGE_NAME>

如果你自己手動(dòng)敲此命令进陡,也會(huì)得到如下相關(guān)信息


這里寫圖片描述

如果我們的項(xiàng)目有自動(dòng)化UI測(cè)試工具愿阐,就可以在構(gòu)建服務(wù)器上在一些UI交互后(列表滑動(dòng),復(fù)雜動(dòng)畫)運(yùn)行此命令趾疚,查看“Janky Frames”等值是否有變化缨历。這樣做能夠幫我們確定最近的幾次提交(commite)是否影響了性能,在產(chǎn)品發(fā)布前發(fā)現(xiàn)和解決此問題糙麦。如果我們使用framestats作為關(guān)鍵字辛孵,還能獲得更多詳細(xì)的渲染信息。
我們還能以其他方式展示此圖
在“Profile GPU Rendering”選項(xiàng)里赡磅,有“On screen as bars”這個(gè)選項(xiàng)魄缚,選中它,在手機(jī)屏幕上就會(huì)出現(xiàn)三個(gè)圖像焚廊,分別代表StatusBar冶匹,NavBar和當(dāng)前程序的Activity的GPU Rending信息,以綠線指示16ms的渲染閾值咆瘟。


這里寫圖片描述

在右側(cè)這張圖嚼隘,我們看到有些幀超過了綠線,即說明渲染時(shí)間超過了16ms袒餐。這些“越界”的幀大部分是藍(lán)色飞蛹,我們大概可以認(rèn)為是因?yàn)槔L制了太多或者太復(fù)雜的View。我滑動(dòng)了一下此界面的信息流灸眼,的確有多種類型的View.有些會(huì)被重繪卧檐,有些比較復(fù)雜。所以那些“越界”的幀可能是因?yàn)檎诶L制復(fù)雜的View.

Hierarchy Viewer

我非常喜歡這個(gè)工具幢炸,很遺憾好多人都沒有使用過泄隔。
Hierarchy Viewer能顯示性能情況,屏幕上完整的View結(jié)構(gòu)宛徊,以及View的屬性佛嬉。如果你單獨(dú)運(yùn)行Hierarchy Viewer,而不是從Android Monitor啟動(dòng)闸天,你也能獲取主題信息暖呕,所有的style屬性女轿。當(dāng)我設(shè)計(jì)以及優(yōu)化布局時(shí)就會(huì)這么做悲立。


這里寫圖片描述

在上圖中間习绢,我們能看到樹形的View結(jié)構(gòu)。View結(jié)構(gòu)可以很寬氧映,但是如果View層數(shù)太深(比如10層左右),就會(huì)增加昂貴的layout和measurement操作拗秘。測(cè)量View時(shí)調(diào)用View.onMeasure, 布局子View時(shí)調(diào)用View.onLayout签舞。這兩個(gè)命令會(huì)向子View傳遞。有些布局會(huì)調(diào)用兩次這兩個(gè)命令戚揭,比如RelativeLayout和某些LinearLayout诱告,如果View是嵌套的,命令傳遞的次數(shù)就會(huì)以指數(shù)增長(zhǎng)民晒。
在右下角精居,是布局實(shí)際的效果,標(biāo)注了每個(gè)View放置的位置潜必,我們可以在此圖或者樹形圖中選中一個(gè)View, 然后在左邊查看所有的屬性靴姿。當(dāng)設(shè)計(jì)布局時(shí),我有時(shí)候不能確定為什么某個(gè)View會(huì)被布局在那里磁滚。有了這個(gè)工具佛吓,我就能在樹形圖上找到它,選中恨旱,然后就能在預(yù)覽圖中看到它的位置辈毯。在設(shè)計(jì)有趣的動(dòng)畫時(shí),我會(huì)查看屏幕中View的最終測(cè)量數(shù)據(jù)搜贤,據(jù)此精確的移動(dòng)View谆沃。我也能發(fā)現(xiàn)被無意蓋掉而看不見的View。


這里寫圖片描述

對(duì)于每一個(gè)View,我們都可以獲知它本身以及子View的measure/layout/draw時(shí)間仪芒。顏色指示它與其他View比較時(shí)唁影,性能情況如何,有助于找出View繪制過程中最慢的一環(huán)掂名。而且我們能看到View的預(yù)覽据沈,能在樹形圖中看到此View創(chuàng)建的步驟,找出和刪除冗余的步驟饺蔑。影響性能的一個(gè)重要原因是Overdraw锌介。
(譯者注:
Android Studio的

File——Settings——Inspections——Android Lint可以設(shè)置檢測(cè)layout的深度和寬度:
勾選 Layout has too many views 和 Layout hierarchy is too deep

Layout has too many views表示控件太多,默認(rèn)超過80個(gè)控件會(huì)提示該問題猾警。

Layout hierarchy is too deep表示布局太深孔祸,默認(rèn)層級(jí)超過10層會(huì)提示該問題,可以自定義環(huán)境變量ANDROID_LINT_MAX_DEPTH來修改发皿。
布局層數(shù)過深會(huì)導(dǎo)致StackOverflowErrors崔慧,因?yàn)閐raw() dispatchDraw() drawChild()等方法都會(huì)消耗stack空間)

Overdraw

正如GPU Profiling所呈現(xiàn)的,如果GPU需要在屏幕上進(jìn)行大量的繪制穴墅,Execute階段(GPU中的黃色部分)會(huì)花費(fèi)更多的時(shí)間惶室,繪制每一幀所需的時(shí)間也就拉長(zhǎng)了温自。在已繪制的屏幕上再度繪制,就叫做過度繪制皇钞,比如在紅色的背景上繪制一個(gè)黃色的按鈕悼泌。GPU需要先繪制紅色的背景,然后繪制黃色的按鈕鹅士,就造成了過度繪制券躁。如果是很多層的過度繪制,GPU的工作負(fù)荷就很重掉盅,就會(huì)影響到16ms的性能指標(biāo)。


這里寫圖片描述

啟用開發(fā)者選項(xiàng)中的“調(diào)試GPU過度繪制”以舒,所有的過度繪制就會(huì)按照嚴(yán)重程度用不同的顏色展示趾痘。1~2層的過度繪制算合理,有小范圍的淺紅也還能接受蔓钟,但是如果紅色的區(qū)域太多永票,就需要注意了。舉幾個(gè)例子:


這里寫圖片描述

左圖中滥沫,有一個(gè)綠色的列表侣集,通常意味著還行,但頂部的覆蓋區(qū)域顯示為紅色兰绣,就需要解決了世分。右圖中,整個(gè)列表都是淺紅缀辩。兩個(gè)圖的列表都不透明臭埋,都有2~3層的過度繪制。一個(gè)可能的原因是持有Activity/Fragment的窗口(window)臀玄,ListView瓢阴,以及每個(gè)ListView的item都有各自的背景。解決的辦法是只設(shè)置一個(gè)背景健无。

注意:默認(rèn)的主題設(shè)置了窗口的背景色荣恐,如果你的Activity包含的不透明的布局能覆蓋全屏幕,你就可以通過移除窗口的背景色來減少過度繪制累贤〉拢可以在主題中設(shè)置,或者在onCreate方法中調(diào)用 getWindow().setBackgroundDrawable(null)

利用 Hierarchy Viewer, 你能導(dǎo)出所有層級(jí)到 PSD 文件畦浓。用Photoshop打開此文件痹束,查看不同的層級(jí),你能發(fā)現(xiàn)布局中所有的過度繪制. 請(qǐng)利用這些信息去掉冗余的過度繪制讶请,不要調(diào)試GPU過度繪制時(shí)顯示綠色就覺得可以了祷嘶,爭(zhēng)取藍(lán)色屎媳。

Alpha

使用透明屬性可能會(huì)影響性能。為了理解這句話论巍,我們來看看當(dāng)給View設(shè)置alpha屬性時(shí)烛谊,會(huì)發(fā)生些什么?初始布局如下圖所示


這里寫圖片描述

此布局包含三個(gè)互相有重疊的ImageView嘉汰。如果對(duì)布局設(shè)置了Alpha屬性丹禀,也就是調(diào)用 setAlpha(),“直接/簡(jiǎn)單粗暴的方案”是對(duì)各個(gè)子View(在此例中是三個(gè)ImageView)設(shè)置Alpha鞋怀。這樣三個(gè)ImageView會(huì)依據(jù)設(shè)置的Alpha重新繪制并進(jìn)入幀緩沖双泪。結(jié)果如下:


這里寫圖片描述

這不是我們想要的。
因?yàn)槊總€(gè)Image都設(shè)置了alpha密似,重疊的ImageView就混合在一起了焙矛。幸運(yùn)的是,安卓系統(tǒng)找到了解決方案残腌。布局將會(huì)被復(fù)制到離屏緩沖村斟,并對(duì)緩沖區(qū)整體設(shè)置Alpha,處理結(jié)果將會(huì)復(fù)制回幀緩沖抛猫。結(jié)果如下:
這里寫圖片描述

但是蟆盹。。這樣做是有代價(jià)的闺金。
先在離屏緩存區(qū)繪制View,然后再繪制幀緩存逾滥,實(shí)際上是增加了一層未被GPU Profiling檢測(cè)到的過度繪制。安卓系統(tǒng)不知道何時(shí)使用此方案掖看,何時(shí)使用之前提到的方案匣距,所以只能默認(rèn)使用此方案。但我們?nèi)杂修k法設(shè)置Alpha并且避免離屏緩存造成的復(fù)雜性哎壳。

  • TextViews - 調(diào)用 setTextColor() 而不是 setAlpha(). 使用alpha 通道設(shè)置字體顏色, 繪制文本時(shí)會(huì)直接使用此alpha毅待。
  • ImageView - 使用 setImageAlpha() 而不是 setAlpha() 。理由同上归榕。
  • 自定義 View - 如果你的自定義View不支持重疊子View, 就無須理會(huì)此合成操作尸红, 因?yàn)樽覸iew不會(huì)像上面的例子混合在一起. 覆寫hasOverlappingRendering() 方法,并返回 false,我們就在告訴安卓系統(tǒng):使用“直接/簡(jiǎn)單粗暴的方案”。如果要自己處理alpha屬性姆蘸,就覆寫onSetAlpha()方法裂问,并返回true。詳細(xì)請(qǐng)參考文章1甜孤,文章2, 文章3

(譯者注:
其他過度繪制優(yōu)化實(shí)踐
Android性能優(yōu)化之減少UI過度繪制
實(shí)戰(zhàn) Android中的UI過度繪制

Hardware Acceleration

安卓蜂巢版引進(jìn)了硬件加速功能铭污,我們由此有了新的繪制模型來渲染APP。硬件加速引入了DisplayList結(jié)構(gòu)喻鳄,通過記錄 View 的繪制命令來加快渲染再菊。但是有一個(gè)很重要的功能是開發(fā)者往往遺漏或者沒有正確的使用的—View layers。
使用View layer竿奏,我們能在離屏緩存中渲染View(就像上面應(yīng)用Alpha通道的例子)袄简,并隨意操作View。這個(gè)功能在動(dòng)畫的時(shí)候很有用泛啸,能讓復(fù)雜的View在動(dòng)畫時(shí)更順暢绿语。 不使用layer的話, 對(duì)View執(zhí)行動(dòng)畫時(shí)會(huì)先改變其屬性(比如 x 坐標(biāo),伸縮比候址,透明度等)然后invalidate. 對(duì)于復(fù)雜的View, invalidate 操作會(huì)傳遞到各個(gè)子View, 各個(gè)子View都會(huì)重繪吕粹,是個(gè)昂貴的操作。而Hardware提供的View layer 則是在GPU中為View生成一個(gè)紋理(Texture)岗仑。利用texture匹耕,某些操作(改變x/y軸坐標(biāo),旋轉(zhuǎn)荠雕,alpha等)就不需要invalidate了稳其。這一切意味著,我們能在一個(gè)復(fù)雜的View上執(zhí)行動(dòng)畫炸卑,而不需要invalidate! 這會(huì)讓動(dòng)畫順暢很多既鞠。下面的例子將告訴你如何做:

// Using the Object animator
view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(view, View.TRANSLATION_X, 20f);
objectAnimator.addListener(new AnimatorListenerAdapter() {
    @Override
    public void onAnimationEnd(Animator animation) {
        view.setLayerType(View.LAYER_TYPE_NONE, null);
    }
});
objectAnimator.start();

// Using the Property animator
view.animate().translationX(20f).withLayer().start();

很簡(jiǎn)單,是不是盖文?
不過使用hardware layer時(shí)需要注意以下幾點(diǎn):

  • Hardware layer 占用了GPU有限的內(nèi)存嘱蛋,所以請(qǐng)只在動(dòng)畫等需要的場(chǎng)合使用Hardware layer,使用完后及時(shí)清理五续。上例中洒敏,使用ObjectAnimator時(shí),我設(shè)置了一個(gè)監(jiān)聽器疙驾,在動(dòng)畫結(jié)束時(shí)移除layer凶伙。使用Property animator時(shí), 我使用了withLayers()方法, 此方法會(huì)在動(dòng)畫開始時(shí)自動(dòng)創(chuàng)建layer并在動(dòng)畫結(jié)束時(shí)移除。

  • 如果你在應(yīng)用了hardware layer以后改變了View的屬性, 就會(huì)invalidate hardware layer 并在離屏緩存重新渲染一遍View荆萤。這種情況在執(zhí)行Hardware Layer未優(yōu)化的操作時(shí)會(huì)發(fā)生 ( 到目前為止, 被優(yōu)化的操作包括: 旋轉(zhuǎn), 伸縮, 坐標(biāo)設(shè)置, 坐標(biāo)偏移, pivot(樞軸) 和 透明度)镊靴。比如 , 你對(duì)使用了hardware layer的View執(zhí)行動(dòng)畫 ,一邊位移一邊更新 View 的背景色链韭,就會(huì)導(dǎo)致hardware layer不停的更新. 在這種情況下偏竟,更新hardware layer的開銷會(huì)抵消掉使用它帶來的好處。

在第二種情況下敞峭,我們可以查看hardware layer更新的情況踊谋。即在開發(fā)者選項(xiàng)中啟用 “顯示硬件層更新”

這里寫圖片描述

啟用后,View在更新其hardware layer時(shí)會(huì)以綠光閃爍旋讹。不久前我的一個(gè)ViewPager滑動(dòng)起來不流暢時(shí)我就啟動(dòng)了此選項(xiàng). 下圖是我當(dāng)時(shí)所看到的:
這里寫圖片描述

在整個(gè)的滑動(dòng)過程中殖蚕,兩個(gè)Page都顯示綠色轿衔!
這意味著有hardware layer被創(chuàng)建,并且滑動(dòng)的過程中頁面被重新渲染睦疫。我的確在滑動(dòng)時(shí)更新了頁面害驹,對(duì)背景使用了視差效果,并且頁面中的內(nèi)容也有漸進(jìn)的動(dòng)畫效果蛤育。但是我并沒有主動(dòng)創(chuàng)建hardware layer宛官。在閱讀了ViewPager的源碼后,我發(fā)現(xiàn)當(dāng)用戶滑動(dòng)時(shí)瓦糕,會(huì)為兩個(gè)頁面創(chuàng)建hardware layer底洗,并且在滑動(dòng)停止時(shí)移除。
盡管滑動(dòng)頁面時(shí)創(chuàng)建hardware layer是合理的行為咕娄,但不適合我的這種情形亥揖。通常情況下,我們并不會(huì)在滑動(dòng)頁面的同時(shí)更新頁面圣勒,而頁面內(nèi)容也往往很復(fù)雜—?jiǎng)?chuàng)建hardware layer就可以幫助渲染的更快费变。在我的這個(gè)應(yīng)用中,卻并不是這樣的圣贸,因此我hack了一下胡控,移掉了hardware layer。
hardware layer并不是銀彈旁趟,理解它是如何工作的,并恰當(dāng)?shù)氖褂煤苤匾诱溃駝t你會(huì)可能會(huì)發(fā)現(xiàn)得不償失锡搜。

DIY

為了演示我提到的這些例子,我寫了很多代碼來模擬情景瞧掺。你能在 Github repositoryGoogle Play找到所有的演示例子耕餐。

更多的信息

隨著Android 操作系統(tǒng)的演進(jìn),你優(yōu)化APP的方式也得跟著變化辟狈。新的Android SDK會(huì)引入新的工具和功能(比如hardware layers)肠缔。跟上趨勢(shì),在改動(dòng)代碼時(shí)權(quán)衡利弊哼转,這很重要明未。
YouTube上有一個(gè)很棒的播放列表, Android Performance Patterns壹蔓,是一系列谷歌出品的小視頻趟妥,闡述了性能優(yōu)化的各個(gè)方面。比如比較不同的數(shù)據(jù)結(jié)構(gòu)(HashMap vs ArrayMa)佣蓉,如何優(yōu)化Bitmaps 披摄,甚至是優(yōu)化網(wǎng)絡(luò)請(qǐng)求亲雪。我強(qiáng)烈建議把播放列表都看一遍。
請(qǐng)加入 Android Performance Patterns Google+ community 疚膊,一起和谷歌工程師在內(nèi)的人士討論性能問題义辕,分享觀點(diǎn),文章和問題寓盗。
更多有趣的鏈接:

  • 了解安卓圖形架構(gòu)如何運(yùn)行灌砖。文章完整的解釋了如下主題:
    Android是如何渲染UI的,不同系統(tǒng)組件的差異和通信(比如SurfaceFlinger)贞让。文章很長(zhǎng)周崭,但值得一讀。
  • Google IO 2012的一則演講
    闡述了繪制模型的工作原理喳张,以及UI失幀的原因续镇。
  • Android Performance Workshop talk,來自Devoxx 2013销部,展示了Android 4.4 引入的繪制模型的優(yōu)化摸航,以及用于優(yōu)化性能的不同工具(Systrace, Overdraw等)
  • 關(guān)于防御性優(yōu)化的好文章,解釋了其與過早優(yōu)化的區(qū)別舅桩。
    很多開發(fā)者不想優(yōu)化代碼酱虎,因?yàn)樗麄冇X得造成的影響不易覺察。請(qǐng)記住擂涛,積少成多會(huì)導(dǎo)致大問題读串。如果有機(jī)會(huì)做一個(gè)小的優(yōu)化,盡管微小撒妈,但請(qǐng)別放棄恢暖。
  • Memory managment in Android
    Google IO 2011的一個(gè)視頻,有點(diǎn)老舊狰右,但仍然有價(jià)值杰捂。說明了安卓如何管理內(nèi)存,以及檢測(cè)工具如Eclipse MAT
  • Case study
    Romain Guy的作品棋蚌,內(nèi)容是優(yōu)化Twitter客戶端嫁佳。介紹了他如何發(fā)現(xiàn)APP的性能問題,也推薦了解決方案谷暮。這里有個(gè) follow-up post蒿往,繼續(xù)跟進(jìn)了此APP的性能優(yōu)化問題。
    我希望你們已經(jīng)獲取了足夠的信息和自信來優(yōu)化你們的APP坷备,并從今天就開始優(yōu)化熄浓。
    trace用起來,開發(fā)者選項(xiàng)用起來,你已經(jīng)走在優(yōu)化的路上了赌蔑。你的任何新發(fā)現(xiàn)俯在,都可以在這留言,或者發(fā)表在Android Performance Patterns Google+ community
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末娃惯,一起剝皮案震驚了整個(gè)濱河市跷乐,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌趾浅,老刑警劉巖愕提,帶你破解...
    沈念sama閱讀 218,451評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異皿哨,居然都是意外死亡浅侨,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門证膨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來如输,“玉大人,你說我怎么就攤上這事央勒〔患” “怎么了?”我有些...
    開封第一講書人閱讀 164,782評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵崔步,是天一觀的道長(zhǎng)稳吮。 經(jīng)常有香客問我,道長(zhǎng)井濒,這世上最難降的妖魔是什么灶似? 我笑而不...
    開封第一講書人閱讀 58,709評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮瑞你,結(jié)果婚禮上喻奥,老公的妹妹穿的比我還像新娘。我一直安慰自己捏悬,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,733評(píng)論 6 392
  • 文/花漫 我一把揭開白布润梯。 她就那樣靜靜地躺著过牙,像睡著了一般。 火紅的嫁衣襯著肌膚如雪纺铭。 梳的紋絲不亂的頭發(fā)上寇钉,一...
    開封第一講書人閱讀 51,578評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音舶赔,去河邊找鬼扫倡。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的撵溃。 我是一名探鬼主播疚鲤,決...
    沈念sama閱讀 40,320評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼缘挑!你這毒婦竟也來了集歇?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,241評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤语淘,失蹤者是張志新(化名)和其女友劉穎诲宇,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體惶翻,經(jīng)...
    沈念sama閱讀 45,686評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡姑蓝,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,878評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了吕粗。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片纺荧。...
    茶點(diǎn)故事閱讀 39,992評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖溯泣,靈堂內(nèi)的尸體忽然破棺而出虐秋,到底是詐尸還是另有隱情,我是刑警寧澤垃沦,帶...
    沈念sama閱讀 35,715評(píng)論 5 346
  • 正文 年R本政府宣布客给,位于F島的核電站,受9級(jí)特大地震影響肢簿,放射性物質(zhì)發(fā)生泄漏靶剑。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,336評(píng)論 3 330
  • 文/蒙蒙 一池充、第九天 我趴在偏房一處隱蔽的房頂上張望桩引。 院中可真熱鬧,春花似錦收夸、人聲如沸坑匠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽厘灼。三九已至,卻和暖如春咽瓷,著一層夾襖步出監(jiān)牢的瞬間设凹,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工茅姜, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留闪朱,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,173評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像奋姿,于是被迫代替她去往敵國(guó)和親锄开。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,947評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容