引言
1. Android性能優(yōu)化篇之內(nèi)存優(yōu)化--內(nèi)存泄漏
2.Android性能優(yōu)化篇之內(nèi)存優(yōu)化--內(nèi)存優(yōu)化分析工具
3.Android性能優(yōu)化篇之UI渲染性能優(yōu)化
4.Android性能優(yōu)化篇之計(jì)算性能優(yōu)化
5.Android性能優(yōu)化篇之電量優(yōu)化(1)——電量消耗分析
6.Android性能優(yōu)化篇之電量優(yōu)化(2)
7.Android性能優(yōu)化篇之網(wǎng)絡(luò)優(yōu)化
8.Android性能優(yōu)化篇之Bitmap優(yōu)化
9.Android性能優(yōu)化篇之圖片壓縮優(yōu)化
10.Android性能優(yōu)化篇之多線程并發(fā)優(yōu)化
11.Android性能優(yōu)化篇之?dāng)?shù)據(jù)傳輸效率優(yōu)化
12.Android性能優(yōu)化篇之程序啟動時間性能優(yōu)化
13.Android性能優(yōu)化篇之安裝包性能優(yōu)化
14.Android性能優(yōu)化篇之服務(wù)優(yōu)化
介紹
在用戶使用APP時,一方面想要華麗炫酷的動畫交互凡傅,一方面需要交互的的流暢運(yùn)行厌小,如何平衡設(shè)計(jì)和性能就需要我們不斷的學(xué)習(xí)和思考了桥温。
UI渲染功能是最普通的功能,那么怎么衡量渲染性能的好壞咕宿?可能出現(xiàn)性能瓶頸的地方有哪些染簇?造成卡頓的原因隧甚?如何解決卡頓?這些都是本章需要思考和解決的的問題峦树。
1.關(guān)于ANR
1.1 什么是ANR?
ANR全名Application Not Responding, 也就是"應(yīng)用無響應(yīng)".當(dāng)操作在一段時間內(nèi)系統(tǒng)無法處理時, 系統(tǒng)層面會彈出ANR對話框.
1.2 產(chǎn)生ANR的原因辣辫?
APP的響應(yīng)是Activity Manage和Window Manage來監(jiān)控的旦事,系統(tǒng)產(chǎn)生ANR的原因:
- 5s內(nèi)無法響應(yīng)用戶輸入事件
- BoradCastReceiver在10s內(nèi)沒有處理結(jié)束
上面兩點(diǎn)的根本原因就是主線程有耗時操作。
1.3 如何避免急灭?
- 耗時操作放到子線程操作
- I/O操作放到子線程
- 避免內(nèi)存泄漏(內(nèi)存不夠也會造成ANR姐浮,當(dāng)時大多數(shù)情況是OOM)
1.4 ANR如何分析?
導(dǎo)出/data/anr/下的traces.txt,發(fā)現(xiàn)日志來定位問題
adb pull data/anr/traces.txt ./
2.怎么衡量渲染性能的好壞葬馋?
2.1 16ms
要知道Android系統(tǒng)每隔16ms就發(fā)出VSYNC信號重新繪制一次Activity,所以要在16ms內(nèi)能夠完成繪制卖鲤,這樣才能達(dá)到每秒60幀,然而這個每秒幀數(shù)的參數(shù)由手機(jī)硬件所決定畴嘶,現(xiàn)在大多數(shù)手機(jī)屏幕刷新率是60赫茲(赫茲是國際單位制中頻率的單位蛋逾,它是每秒中的周期性變動重復(fù)次數(shù)的計(jì)量),也就是說我們有16ms(1000ms/60次=16.66ms)的時間去完成每幀的繪制邏輯操作窗悯,就不會出現(xiàn)卡頓的現(xiàn)象区匣,如果沒有完成,則會丟幀導(dǎo)致卡頓蒋院。
image1.png
image2.png
3.關(guān)于渲染管線
Android系統(tǒng)的渲染管線分為兩個關(guān)鍵組件:CPU和GPU沉颂,它們共同工作,在屏幕上繪制圖片悦污,每個組件都有自身定義的特定流程铸屉。我們必須遵守這些特定的操作規(guī)則才能達(dá)到效果。
CPU負(fù)責(zé)包括Measure切端,Layout彻坛,Record,Execute的計(jì)算操作踏枣,GPU負(fù)責(zé)Rasterization(柵格化)操作昌屉。
在CPU方面,最常見的性能問題是不必要的布局和失效茵瀑,這些內(nèi)容必須在視圖層次結(jié)構(gòu)中進(jìn)行測量间驮、清除并重新創(chuàng)建,引發(fā)這種問題通常有兩個原因:一是重建顯示列表的次數(shù)太多马昨,二是花費(fèi)太多時間作廢視圖層次并進(jìn)行不必要的重繪竞帽,這兩個原因在更新顯示列表或者其他緩存GPU資源時導(dǎo)致CPU工作過度。
在GPU方面鸿捧,最常見的問題是我們所說的過度繪制(overdraw)屹篓,通常是在像素著色過程中,通過其他工具進(jìn)行后期著色時浪費(fèi)了GPU處理時間匙奴。
3.1 GPU
了解Android中如何使用GPU進(jìn)行畫面的渲染可以幫助我們更好的理解性能問題堆巧。我們的布局文件是如何被繪制到屏幕上的?
Resterization柵格化是繪制那些Button,Shape谍肤,Path啦租,String,Bitmap等組件最基礎(chǔ)的操作荒揣。它把那些組件拆分到不同的像素上進(jìn)行顯示刷钢。這是一個很費(fèi)時的操作,GPU的引入就是為了加快柵格化的操作乳附。
image5.png
GPU使用一些指定的基礎(chǔ)指令集内地,主要是多邊形和紋理,也就是圖片赋除,CPU在屏幕上繪制圖像前會向GPU輸入這些指令阱缓,這一過程通常使用的API就是Android的OpenGL ES,這就是說举农,在屏幕上繪制UI對象時無論是按鈕荆针、路徑或者復(fù)選框,都需要在CPU中首先轉(zhuǎn)換為多邊形或者紋理颁糟,然后再傳遞給GPU進(jìn)行格柵化航背。
UI對象轉(zhuǎn)換為一系列多邊形和紋理的過程肯定相當(dāng)耗時,從CPU上傳處理數(shù)據(jù)到GPU同樣也很耗時棱貌。所以很明顯玖媚,我們需要盡量減少對象轉(zhuǎn)換的次數(shù),以及上傳數(shù)據(jù)的次數(shù)婚脱,幸虧今魔,OpenGL ES API允許數(shù)據(jù)上傳到GPU后可以對數(shù)據(jù)進(jìn)行保存,當(dāng)我們下次繪制一個按鈕時障贸,只需要在GPU存儲器里引用它错森,然后告訴OpenGL如何繪制就可以了,一條經(jīng)驗(yàn)之談:渲染性能的優(yōu)化就是盡可能地上傳數(shù)據(jù)到GPU篮洁,然后盡可能長地在不修改的情況下保存數(shù)據(jù)涩维,因?yàn)槊看紊蟼髻Y源到GPU時,我們都會浪費(fèi)寶貴的處理時間.
為了能夠使得App流暢袁波,我們需要在每幀16ms以內(nèi)處理完所有的CPU與GPU的計(jì)算瓦阐,繪制,渲染等等操作锋叨。
4.Hierarchy Viewer工具介紹
Hierarchy Viewer可以很直接的呈現(xiàn)布局的層次關(guān)系垄分,視圖組件的各種屬性。 我們可以通過紅娃磺,黃,綠三種不同的顏色來區(qū)分布局的Measure叫倍,Layout偷卧,Executive的相對性能表現(xiàn)如何豺瘤。
使用步驟?
(1).打開Android Device Monitor
(2).選擇Hierarchy Viewer選項(xiàng)卡
4.1 設(shè)備連接問題
如果你是用的模擬器或者開發(fā)版手機(jī)的話則可以直接進(jìn)行連接調(diào)試了听诸,如果不是的話坐求,官方提供了兩種方式,進(jìn)行連接真機(jī)調(diào)試:
第一種晌梨,通過第三方庫桥嗤,安裝和配置ViewServer,也是目前我在使用的方式,工具地址:點(diǎn)擊仔蝌,步驟如下:
(1).添加依賴
project 下的 build.gradle
allprojects {
repositories {
jcenter()
maven { url "https://jitpack.io" }
}
}
module 下的 build.gradle
dependencies {
...
compile 'com.github.romainguy:ViewServer:017c01cd512cac3ec054d9eee05fc48c5a9d2de'
}
(2).申請權(quán)限
<uses-permission android:name="android.permission.INTERNET"/>
(3).添加代碼
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ViewServer.get(this).addWindow(this);
}
public void onDestroy() {
super.onDestroy();
ViewServer.get(this).removeWindow(this);
}
public void onResume() {
super.onResume();
ViewServer.get(this).setFocusedWindow(this);
}
第二種泛领,通過設(shè)置環(huán)境變量,export ANDROID_HVPROTO=ddm(可能對小米手機(jī)無用)
4.2 性能表示
這里我們主要關(guān)注下面的三個圓圈敛惊,從左到右依次渊鞋,代表View的Measure, Layout和Draw的性能,不同顏色代表不同的性能等級:
(1). 綠: 表示該View的此項(xiàng)性能比該View Tree中超過50%的View都要快瞧挤;例如,一個綠點(diǎn)的測量時間意味著這個視圖的測量時間快于樹中的視圖對象的50%锡宋。
(2). 黃: 表示該View的此項(xiàng)性能比該View Tree中超過50%的View都要慢;例如,一個黃點(diǎn)布局意味著這種觀點(diǎn)有較慢的布局時間超過50%的樹視圖對象特恬。
(3). 紅: 表示該View的此項(xiàng)性能是View Tree中最慢的执俩;例如,一個紅點(diǎn)的繪制時間意味著花費(fèi)時間最多的這一觀點(diǎn)在樹上畫所有的視圖對象。
5.問題分析以及解決方案
5.1 CPU
上面已經(jīng)分析過了癌刽,CPU常見的性能問題是不必要的布局和失效奠滑,引發(fā)這種問題通常有兩個原因:一是重建顯示列表的次數(shù)太多,二是花費(fèi)太多時間作廢視圖層次并進(jìn)行不必要的重繪妒穴。
5.1.1 布局失效優(yōu)化
Android需要把XML布局文件轉(zhuǎn)換成GPU能夠識別并繪制的對象宋税。這個操作是在DisplayList的幫助下完成的。DisplayList持有所有將要交給GPU繪制到屏幕上的數(shù)據(jù)信息,還有執(zhí)行繪制操作的OpenGL命令列表讼油。在某個View第一次需要被渲染時杰赛,Display List會因此被創(chuàng)建,當(dāng)這個View要顯示到屏幕上時矮台,我們會執(zhí)行GPU的繪制指令來進(jìn)行渲染乏屯。
那么第二次渲染這個view會發(fā)生什么呢?1.如果View的Property屬性發(fā)生了改變(例如移動位置)瘦赫,我們就僅僅需要Execute Display List就夠了.
2.如果你修改了View中的某些可見組件的內(nèi)容辰晕,那么之前的DisplayList就無法繼續(xù)使用了,我們需要重新創(chuàng)建一個DisplayList并重新執(zhí)行渲染指令更新到屏幕上确虱。任何時候View中的繪制內(nèi)容發(fā)生變化時含友,都會需要重新創(chuàng)建DisplayList,渲染DisplayList,更新到屏幕上等一系列操作窘问。這個流程的表現(xiàn)性能取決于你的View的復(fù)雜程度辆童,View的狀態(tài)變化以及渲染管道的執(zhí)行性能。
3.如果某個View的大小需要增大到目前的兩倍惠赫,在增大View大小之前把鉴,需要通過父View重新計(jì)算并擺放其他子View的位置。修改View的大小會觸發(fā)整個HierarcyView的重新計(jì)算大小的操作儿咱。
5.1.2 嵌套結(jié)構(gòu)優(yōu)
提升布局性能的關(guān)鍵點(diǎn)是盡量保持布局層級的扁平化庭砍,避免出現(xiàn)重復(fù)的嵌套布局。
我們先來看下列子混埠,然后再來總結(jié):
當(dāng)前頁面有兩個條目怠缸,上面條目是使用LinearLayout中嵌套LinearLayout實(shí)現(xiàn)的,下面條目使用一個RelativeLayout實(shí)現(xiàn)
image13.png
我們使用Hierarchy Viewer工具來看下:
image11.png
現(xiàn)在我們把上面一個條目改成和下面條目一樣的實(shí)現(xiàn)岔冀,看下優(yōu)化后的效果:
image12.png
5.1.2.1 結(jié)果分析
紅色節(jié)點(diǎn)是代表應(yīng)用性能慢的一個潛在問題凯旭,下面是幾個例子,如何來分析和解釋紅點(diǎn)的出現(xiàn)原因使套?
(1).如果在葉節(jié)點(diǎn)或者ViewGroup中罐呼,只有極少的子節(jié)點(diǎn),這可能反映出一個問題侦高,應(yīng)用可能在設(shè)備上運(yùn)行并不慢嫉柴,但是你需要指導(dǎo)為什么這個節(jié)點(diǎn)是紅色的,可以借助Systrace或者Traceview工具奉呛,獲取更多額外的信息
(2).如果一個視圖組里面有許多的子節(jié)點(diǎn)计螺,并且測量階段呈現(xiàn)為紅色,則需要觀察下子節(jié)點(diǎn)的繪制情況
(3).如果視圖層級結(jié)構(gòu)中的根視圖瞧壮,Messure階段為紅色登馒,Layout階段為紅色,Draw階段為黃色咆槽,這個是比較常見的陈轿,因?yàn)檫@個節(jié)點(diǎn)是所有其它視圖的父類
(4).如果視圖結(jié)構(gòu)中的一個葉子節(jié)點(diǎn),有20個視圖是紅色的Draw階段秦忿,這是有問題的麦射,需要檢查代碼里面的onDraw方法,不應(yīng)該在那里調(diào)用
5.1.2.2 優(yōu)化建議
(1).沒有用的父布局時指沒有背景繪制或者沒有大小限制的父布局灯谣,這樣的布局不會對UI效果產(chǎn)生任何影響潜秋。我們可以把沒有用的父布局,通過<merge/>標(biāo)簽合并來減少UI的層次
(2).使用線性布局LinearLayout排版導(dǎo)致UI層次變深胎许,如果有這類問題峻呛,我們就使用相對布局RelativeLayout代替LinearLayout,減少UI的層次
(3).不常用的UI被設(shè)置成GONE,比如異常的錯誤頁面罗售,如果有這類問題,我們需要用<ViewStub/>標(biāo)簽杀饵,代替GONE提高UI性能
5.1.3 常用的優(yōu)化示例
(1). include 標(biāo)簽
include標(biāo)簽常用于將布局中的公共部分提取出來供其他layout共用莽囤,以實(shí)現(xiàn)布局模塊化谬擦,這在布局編寫方便提供了大大的便利切距。
(2). viewstub 標(biāo)簽
viewstub標(biāo)簽同include標(biāo)簽一樣可以用來引入一個外部布局,不同的是惨远,viewstub引入的布局默認(rèn)不會擴(kuò)張谜悟,即既不會占用顯示也不會占用位置,從而在解析layout時節(jié)省cpu和內(nèi)存北秽。
viewstub常用來引入那些默認(rèn)不會顯示葡幸,只在特殊情況下顯示的布局,如進(jìn)度布局贺氓、網(wǎng)絡(luò)失敗顯示的刷新布局蔚叨、信息出錯出現(xiàn)的提示布局等。
//第一種
ViewStub stub = (ViewStub)findViewById(...)
View stubView= stub.inflate();
//根據(jù)實(shí)際情況辙培,顯示
stubView.setVisibility()
//第二種
View viewStub = findViewById(R.id.network_error_layout);
viewStub.setVisibility(View.VISIBLE); // ViewStub被展開后的布局所替換
注意:ViewStub所加載的布局是不可以使用<merge>標(biāo)簽的
(3). merge 標(biāo)簽
在使用了include后可能導(dǎo)致布局嵌套過多蔑水,多余不必要的layout節(jié)點(diǎn),從而導(dǎo)致解析變慢
merge標(biāo)簽可用于兩種典型情況:
(1). 布局頂結(jié)點(diǎn)是FrameLayout且不需要設(shè)置background或padding等屬性扬蕊,可以用merge代替搀别,因?yàn)锳ctivity內(nèi)容試圖的parent view就是個FrameLayout,所以可以用merge消除只剩一個尾抑。
(2). 某布局作為子布局被其他布局include時歇父,使用merge當(dāng)作該布局的頂節(jié)點(diǎn),這樣在被引入時頂結(jié)點(diǎn)會自動被忽略再愈,而將其子節(jié)點(diǎn)全部合并到主布局中榜苫。
5.2 GPU
在GPU方面,最常見的問題是我們所說的過度繪制(overdraw)翎冲,通常是在像素著色過程中垂睬,通過其他工具進(jìn)行后期著色時浪費(fèi)了GPU處理時間。
過度繪制描述的是屏幕上的某個像素在同一幀的時間內(nèi)被繪制了多次府适。在多層次重疊的UI結(jié)構(gòu)里面羔飞,如果不可見的UI也在做繪制的操作,會導(dǎo)致某些像素區(qū)域被繪制了多次檐春。這樣就會浪費(fèi)大量的CPU以及GPU資源逻淌。
當(dāng)設(shè)計(jì)上追求更華麗的視覺效果的時候,我們就容易陷入采用復(fù)雜的多層次重疊視圖來實(shí)現(xiàn)這種視覺效果的怪圈疟暖。這很容易導(dǎo)致大量的性能問題卡儒,為了獲得最佳的性能田柔,我們必須盡量減少Overdraw的情況發(fā)生。幸運(yùn)的是骨望,我們可以通過手機(jī)設(shè)置里面的開發(fā)者選項(xiàng)硬爆,打開Show GPU Overdraw的選項(xiàng),觀察UI上的Overdraw情況擎鸠。
image15.png
GPU Profiling
從Android M系統(tǒng)開始缀磕,系統(tǒng)更新了GPU Profiling的工具來幫助我們定位UI的渲染性能問題。早期的CPU Profiling工具只能粗略的顯示出Process劣光,Execute袜蚕,Update三大步驟的時間耗費(fèi)情況。
image1.jpg
但是僅僅顯示三大步驟的時間耗費(fèi)情況绢涡,還是不太能夠清晰幫助我們定位具體的程序代碼問題牲剃,所以在Android M版本開始,GPU Profiling工具把渲染操作拆解成如下8個詳細(xì)的步驟進(jìn)行顯示雄可。
image2.jpg
舊版本中提到的Proces凿傅,Execute,Update還是繼續(xù)得到了保留数苫,他們的對應(yīng)關(guān)系如下:
image3.jpg
- Sync & Upload:通常表示的是準(zhǔn)備當(dāng)前界面上有待繪制的圖片所耗費(fèi)的時間聪舒,為了減少該段區(qū)域的執(zhí)行時間,我們可以減少屏幕上的圖片數(shù)量或者是縮小圖片本身的大小文判。
- Measure & Layout:這里表示的是布局的onMeasure與onLayout所花費(fèi)的時間过椎,一旦時間過長,就需要仔細(xì)檢查自己的布局是不是存在嚴(yán)重的性能問題戏仓。
- Animation:表示的是計(jì)算執(zhí)行動畫所需要花費(fèi)的時間疚宇,包含的動畫有ObjectAnimator,ViewPropertyAnimator赏殃,Transition等等敷待。一旦這里的執(zhí)行時間過長,就需要檢查是不是使用了非官方的動畫工具或者是檢查動畫執(zhí)行的過程中是不是觸發(fā)了讀寫操作等等仁热。
- Input Handling:表示的是系統(tǒng)處理輸入事件所耗費(fèi)的時間榜揖,粗略等于對于的事件處理方法所執(zhí)行的時間。一旦執(zhí)行時間過長抗蠢,意味著在處理用戶的輸入事件的地方執(zhí)行了復(fù)雜的操作举哟。
- Misc/Vsync Delay:如果稍加注意,我們可以在開發(fā)應(yīng)用的Log日志里面看到這樣一行提示:I/Choreographer(691): Skipped XXX frames! The application may be doing too much work on its main thread迅矛。這意味著我們在主線程執(zhí)行了太多的任務(wù)妨猩,導(dǎo)致UI渲染跟不上vSync的信號而出現(xiàn)掉幀的情況。
上面八種不同的顏色區(qū)分了不同的操作所耗費(fèi)的時間秽褒,為了便于我們迅速找出那些有問題的步驟壶硅,GPU Profiling工具會顯示16ms的閾值線威兜,這樣就很容易找出那些不合理的性能問題,再仔細(xì)看對應(yīng)具體哪個步驟相對來說耗費(fèi)時間比例更大庐椒,結(jié)合上面介紹的細(xì)化步驟椒舵,從而快速定位問題,修復(fù)問題约谈。
5.2.1 優(yōu)化建議
(1).移除Window默認(rèn)的Background
getWindow().setBackgroundDrawable(null);
(2).移除XML布局文件中非必需的Background
(3).按需顯示占位背景圖片
在給ImageView設(shè)置圖片時笔宿,判斷是否獲取到對應(yīng)的Bitmap,在獲取到圖像之后窗宇,把ImageView的Background設(shè)置為Transparent措伐,只有當(dāng)圖像沒有獲取到的時候才設(shè)置對應(yīng)的Background占位圖片特纤,這樣可以避免因?yàn)樵O(shè)置背景圖而導(dǎo)致的過度渲染军俊。
(4).剪輯不顯示的UI組件
對不可見的UI組件進(jìn)行繪制更新會導(dǎo)致Overdraw。例如Nav Drawer從前置可見的Activity滑出之后捧存,如果還繼續(xù)繪制那些在Nav Drawer里面不可見的UI組件粪躬,這就導(dǎo)致了Overdraw。為了解決這個問題昔穴,Android系統(tǒng)會通過避免繪制那些完全不可見的組件來盡量減少Overdraw镰官。那些Nav Drawer里面不可見的View就不會被執(zhí)行浪費(fèi)資源。
image16.png
但是不幸的是吗货,對于那些過于復(fù)雜的自定義的View(通常重寫了onDraw方法)泳唠,Android系統(tǒng)無法檢測在onDraw里面具體會執(zhí)行什么操作,系統(tǒng)無法監(jiān)控并自動優(yōu)化宙搬,也就無法避免Overdraw了笨腥。但是我們可以通過canvas.clipRect()來幫助系統(tǒng)識別那些可見的區(qū)域。這個方法可以指定一塊矩形區(qū)域勇垛,只有在這個區(qū)域內(nèi)才會被繪制脖母,其他的區(qū)域會被忽視。這個API可以很好的幫助那些有多組重疊組件的自定義View來控制顯示的區(qū)域闲孤。同時clipRect方法還可以幫助節(jié)約CPU與GPU資源谆级,在clipRect區(qū)域之外的繪制指令都不會被執(zhí)行,那些部分內(nèi)容在矩形區(qū)域內(nèi)的組件讼积,仍然會得到繪制肥照。
除了clipRect方法之外,我們還可以使用canvas.quickreject()來判斷是否沒和某個矩形相交勤众,從而跳過那些非矩形區(qū)域內(nèi)的繪制操作舆绎。
下面我們來看個實(shí)例:
image17.png
代碼:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mCardList != null && mCardList.size() > 0) {
for (int i = 0; i < mCardList.size(); i++) {
mCardLeft = i * mCardSpacing;
drawCard(canvas, mCardList.get(i), mCardLeft, 0);
}
}
}
private void drawCard(Canvas canvas, CardItem card, int left, int top) {
Bitmap mBitmap = getBitmap(card.resId);
canvas.drawBitmap(mBitmap, left, top, mPaint);
}
private Bitmap getBitmap(int resId) {
return BitmapFactory.decodeResource(this.getResources(), resId);
}
我們看到撲克牌有不可見的區(qū)域但是還是被繪制了,導(dǎo)致過度繪制决摧。下面我們進(jìn)行剪輯亿蒸。
剪輯過后效果:
image18.png
代碼:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mCardList != null && mCardList.size() > 0) {
for (int i = 0; i < mCardList.size()-1; i++) {
mCardLeft = i * mCardSpacing;
canvas.save();
canvas.clipRect(mCardLeft,
0f,
mCardLeft + mCardSpacing,
mCardList.get(i).getHeight());
drawCard(canvas, mCardList.get(i), mCardLeft, 0);
canvas.restore();
}
drawCard(canvas, mCardList.get(mCardList.size()-1), mCardLeft + mCardSpacing, 0);
}
}
注意:有些過度繪制對于運(yùn)行性能凑兰,可能是必要的也是可以接受的,比如說Android的ActionBar边锁,但是姑食,如果我們希望應(yīng)用體驗(yàn)更進(jìn)一步,我們可以考慮盡可能地減少過度繪制茅坛。
5.3 其他問題引起的卡頓分析
5.3.1 內(nèi)存抖動
內(nèi)存抖動是因?yàn)樵诙虝r間內(nèi)大量的對象被創(chuàng)建又馬上被釋放音半。瞬間產(chǎn)生大量的對象會嚴(yán)重占用Young Generation的內(nèi)存區(qū)域,當(dāng)達(dá)到閥值贡蓖,剩余空間不夠的時候曹鸠,會觸發(fā)GC從而導(dǎo)致剛產(chǎn)生的對象又很快被回收。即使每次分配的對象占用了很少的內(nèi)存斥铺,但是他們疊加在一起會增加Heap的壓力捣作,從而觸發(fā)更多其他類型的GC慰安。這個操作有可能會影響到幀率,并使得用戶感知到性能問題(卡頓)。
image19.png
解決上面的問題有簡潔直觀方法蔓钟,如果你在Memory Monitor里面查看到短時間發(fā)生了多次內(nèi)存的漲跌蕴轨,這意味著很有可能發(fā)生了內(nèi)存抖動词爬。
image20.png同時我們還可以通過Allocation Tracker來查看在短時間內(nèi)镐躲,同一個棧中不斷進(jìn)出的相同對象。這是內(nèi)存抖動的典型信號之一岖常。
當(dāng)你大致定位問題之后驯镊,接下去的問題修復(fù)也就顯得相對直接簡單了。例如竭鞍,你需要避免在for循環(huán)里面分配對象占用內(nèi)存板惑,需要嘗試把對象的創(chuàng)建移到循環(huán)體之外,自定義View中的onDraw方法也需要引起注意笼蛛,每次屏幕發(fā)生繪制以及動畫執(zhí)行過程中洒放,onDraw方法都會被調(diào)用到,避免在onDraw方法里面執(zhí)行復(fù)雜的操作滨砍,避免創(chuàng)建對象往湿。對于那些無法避免需要創(chuàng)建對象的情況,我們可以考慮對象池模型惋戏,通過對象池來解決頻繁創(chuàng)建與銷毀的問題领追,但是這里需要注意結(jié)束使用之后,需要手動釋放對象池中的對象响逢。