讀書筆記:Android應(yīng)用性能優(yōu)化最佳實(shí)踐
一止后、影響卡頓的基本原因
1.繪制任務(wù)太重
2.主線程任務(wù)耗時(shí)太長
二抄课、頁面繪制的流程
CPU準(zhǔn)備數(shù)據(jù)---GPU從緩存列表獲取數(shù)據(jù)----Display顯示數(shù)據(jù)
三窑睁、性能優(yōu)化檢測工具
1.Profile GPU rendering :GPU呈現(xiàn)模式分析
或是使用dumpsys命令更直觀的查看繪制的耗時(shí)
adb shell dumpsys gfxinfo com.efrobot.robot.video
2.Systrace UI
分析UI的性能
參考Systrace
3.TraceView
分析函數(shù)的執(zhí)行過程和耗時(shí)時(shí)間
參考TraceView
4.Hierarchy Viewer
查看Layout的嵌套以及繪制的時(shí)間
5.Java Heap
查看內(nèi)存分配
6.Allocation Trace
觀察一段時(shí)間內(nèi),內(nèi)存分配的次數(shù)和類
7.Show GPU Overdraw
手機(jī)檢查過渡繪制
四、布局優(yōu)化
1.減少層級(jí)
合理的使用布局
RelativeLayout:繪制層級(jí)少拴泌,可以替代LinearLayout多嵌套才能實(shí)現(xiàn)的效果,但是性能差惊橱,因?yàn)橐獧M向蚪腐,縱向測量兩次。
LinearLayout:測量少税朴,但是實(shí)現(xiàn)復(fù)雜布局需要多次嵌套回季。
使用Merge標(biāo)簽減少布局,優(yōu)化布局層級(jí)
1.在Activity整體的布局中正林,跟布局元素要是FrameLayout
2.必須為該布局指定一個(gè)ViewGroup泡一,并且attachToRoot為true
3.不能在ViewStub中使用
使用ViewStub提高顯示速度:當(dāng)布局在加載時(shí)不是所有所有元素都要顯示時(shí),可以使用
ViewStub只能加載布局
加載一次后就不能再對(duì)布局做操作
避免默認(rèn)主題的背景:使用activity的自動(dòng)主題時(shí)會(huì)有一個(gè)默認(rèn)背景觅廓,由DecorView持有
this.getWindow().setBackground(null)
五鼻忠、啟動(dòng)耗時(shí)優(yōu)化
1.獲取activity的啟動(dòng)時(shí)間
使用ADB命令
adb shell am start -W [packageName]/packageName.ActivityName
2.代碼打點(diǎn)
Application,Activity聲明周期打點(diǎn)
數(shù)據(jù)庫操作打點(diǎn)
其他的耗時(shí)業(yè)務(wù)打點(diǎn)
3.優(yōu)化的方向
1.UI布局:檢查是否過渡繪制杈绸,布局嵌套帖蔓,掉幀嚴(yán)重
2.邏輯優(yōu)化:異步加載,延期加載瞳脓,分布加載
3.避免后臺(tái)線程操作造成頻繁的GC:例如在ListView滑動(dòng)時(shí)禁止使用后臺(tái)線程下載圖片
4.使用TraceView查看updateDisplayListIfDirty方法塑娇,最后畫面刷新到屏幕上需要調(diào)用DisplayList方法
六、動(dòng)畫優(yōu)化
1.使用屬性動(dòng)畫優(yōu)化刷新率劫侧,盡量避免補(bǔ)間動(dòng)畫
2.在動(dòng)畫上使用硬件加速(慎重使用 )
七埋酬、內(nèi)存優(yōu)化
GC的過程中,任何工作線程都將停止,GC的時(shí)間越長奇瘦,卡頓越明顯棘催,還會(huì)造成OOM問題。
1.對(duì)象的幾個(gè)階段:創(chuàng)建---使用---不可達(dá)---不可見--不可達(dá)---收集---回收
不可見:無法找到引用該對(duì)象的引用關(guān)系耳标,但可能被靜態(tài)變量醇坝,JNI層強(qiáng)引用,無法回收
2.內(nèi)存分配
ART和Dalvik虛擬機(jī)在內(nèi)存分配
LinearAlloc:存儲(chǔ)虛擬機(jī)中的類以及永久數(shù)據(jù)次坡,一塊只讀的空間
Zygote Space:
Allocation Space:分配內(nèi)存區(qū)域
Image Space:負(fù)責(zé)分配預(yù)加載類
Large Space:負(fù)責(zé)分配大對(duì)象地址
3.內(nèi)存回收機(jī)制
年輕區(qū)---老年區(qū)--持久區(qū)
新生對(duì)象首先在eden區(qū)創(chuàng)建呼猪,當(dāng)eden區(qū)滿時(shí),會(huì)將存活的內(nèi)存放到s0區(qū)砸琅,s0區(qū)滿時(shí)宋距,會(huì)將此時(shí)還活著的對(duì)象移動(dòng)到s1區(qū)域s0清零,當(dāng)s1區(qū)域也滿時(shí)症脂,會(huì)復(fù)制s1中的數(shù)據(jù)到s0谚赎,反復(fù)執(zhí)行幾次后仍然活著的對(duì)象會(huì)放到老年代。在老年代經(jīng)理過幾次gc仍然存活的對(duì)象會(huì)被移動(dòng)到 持久區(qū)中诱篷。
4.使用Allocation Tracker查看內(nèi)存分配
Allocation Tracker可以查看一段時(shí)間內(nèi)內(nèi)存分配的次數(shù)壶唤,和分配的大小,詳見參考文章
https://blog.csdn.net/itfootball/article/details/48735041
https://blog.csdn.net/itfootball/article/details/48750849
5.分析Hprof文件
通過AndroidStudio生成Hprof文件棕所,觀察視圖闸盔,使用Merge Shortest Path to GC roots,查看合并節(jié)點(diǎn)后,是否還有可達(dá)到該對(duì)象的路徑琳省,如果有那么表示該對(duì)象無法回收迎吵,可能出現(xiàn)內(nèi)存泄漏
八、引用區(qū)分
1.強(qiáng)引用:不會(huì)被GC回收针贬,回收不了會(huì)OOM
2.軟引用:在虛擬機(jī)報(bào)告內(nèi)存溢出前击费,如果內(nèi)存不足回收對(duì)象
3.弱引用:GC就回收
九、內(nèi)存優(yōu)化
1.在1000數(shù)量級(jí)以內(nèi)使用ArrayMap
2.使用基本類型加@IntDef和@StringDef類代替枚舉
3.使用LruCache管理圖片
4.圖片內(nèi)存優(yōu)化
在android上加載圖片桦他,需要將圖片加載成位圖(由多個(gè)像素點(diǎn)組成的圖)荡灾,再解鎖成位圖后,內(nèi)存=寬x高x單位內(nèi)像素瞬铸。圖片被處理32bit/像素的位圖(ARGB_8888)批幌,紅,綠嗓节,藍(lán)荧缘,透明各占8bit。即使圖片不存在透明區(qū)也會(huì)分配8bit的透明區(qū)拦宣。
5.可以修改圖片的格式
RGB_565(16bit)截粗,ARGB_4444(16bit)信姓,ALPHA_8(8bit)。小屏手機(jī)且對(duì)圖像要求不高可以使用rgb绸罗,圓角頭像可以使用ARGB4444,.
BitmapFactory.Options option =new BitmapFactory.Options()
options.inPreferredConfig=Bitmap.Config.RGB_565
BitmapFactory.decodeStream(is,null,options)
6.inSampleSize,inDensity,inTargetDensity:
如果圖片在內(nèi)存中的大小遠(yuǎn)遠(yuǎn)大于使用大小可以使用inSampleSize來壓縮圖片意推,inScaled 會(huì)按照現(xiàn)有密度重新劃分目標(biāo)密度,重新計(jì)算圖片大小.
使用inSampleSize壓縮圖片珊蟀,
BitmapFactory.Options option =new BitmapFactory.Options()
option.inJustDecodeBounds=true;
BitmapFactory.decodeStream(is,null,options)
options.inScaled=true菊值;
options.inDensity=options.outWidth;0
options.inSampleSize=40
options.inTargetDensity=dstWidth*options.inSampleSize;
options.inJustDecode=false;
6.使用三級(jí)緩存對(duì)大量圖片進(jìn)行優(yōu)化
內(nèi)存--本地--網(wǎng)絡(luò)
1.使用LruCache
將最近使用過對(duì)象的強(qiáng)引用放到LinkedHashMap中,將最近最少使用的對(duì)象移除育灸。
2.內(nèi)存復(fù)用
使用Bitmap的options對(duì)象的inBitmap參數(shù):在LruCache移除圖片后腻窒,可以將移除的圖片加入到復(fù)用集合中,當(dāng)需要加載新的圖片時(shí)磅崭,先查找內(nèi)存儿子,再查找復(fù)用集合中是否有符合的圖片。
Bitmap inBitmap=cache.getBitmapFromReusableSet()
if(inBitmap!=null){
options.inBitmap=inBitmap砸喻;
}
3.使用磁盤緩存
使用DiskLruCache
十柔逼、String的優(yōu)化
參考:http://www.androidchina.net/5940.html
String的值是存儲(chǔ)在常量池(在編譯時(shí)期將值保存在.class文件中)中的,一旦創(chuàng)建將不可以修改割岛。
十一卒落、存儲(chǔ)優(yōu)化
1.SharePreference優(yōu)化
Editor的commit是同步寫入,apply是異步寫入蜂桶。在不需要返回值的情況下,使用apply能夠極大的提升性能
SharePreference的的put和getEditor()會(huì)鎖定Editor對(duì)象也切,所以大量寫入SharePreference對(duì)象最后提前獲得一個(gè)Editor對(duì)象扑媚,并且通過標(biāo)志位判斷是否需要讀寫。
if(開關(guān)沒有改變&&沒有發(fā)生變化){
}
2.數(shù)據(jù)庫優(yōu)化
1.使用SQLiteStatement來執(zhí)行插入操作
SQLiteStatement statement=getSqliteDB().compileStatement(STR_INSERT_STATEMENT_CONTACTS)雷恃;
statement.clearBindings();
statement.bind..();
2.更好的方法是使用事物
在數(shù)據(jù)庫插入代碼中疆股,如果沒有顯示的使用事物,系統(tǒng)會(huì)自動(dòng)創(chuàng)建一個(gè)事物倒槐,如果插入的頻繁就會(huì)頻繁的創(chuàng)建事物旬痹。所以顯示的創(chuàng)建一次事物,可以提高效率讨越。
getSqliteDB().beginTranscation();
for(){
getSqliteDB().insert(...)
}
getSqliteDB().setTranscationSuccessful();
getSqliteDB().endTranscation();
十二两残、Service的包活
1.守護(hù)進(jìn)程
2.網(wǎng)絡(luò)連接保持和Service的交互
3.使用SyncAdapter:是一個(gè)系統(tǒng)服務(wù),通過系統(tǒng)的定時(shí)器更新數(shù)據(jù)到ContentProvider,工作在一個(gè)獨(dú)立的進(jìn)程把跨,屬于核心進(jìn)程級(jí)別人弓。使用它本身也會(huì)提高進(jìn)程級(jí)別
十三、耗電優(yōu)化
1.android5.0提供了Battery Historian查看電量使用
2.使用命令
//獲取電量權(quán)限着逐,并重置電量日志
adb shell dumpsys batterystats --enable full-wake-history
shell dumpsys batterystats --reset
//保存數(shù)據(jù)
adb bugreport >bugreport.txt
將文件轉(zhuǎn)化為html崔赌,需要下載battery-historian
python historian.py -a bugreport.txt >battery.html
3.注意消耗:使用WakeLock一定要記得釋放
4.使用JobScheduler執(zhí)行部分任務(wù)
重要不緊急的任務(wù)
耗電量較大的任務(wù)
可以批量執(zhí)行的任務(wù)
十二意蛀、代碼審查
1.單一職責(zé),一個(gè)模塊只負(fù)責(zé)一件事
2.對(duì)象的可擴(kuò)展開放健芭,可修改關(guān)閉
3.代碼復(fù)用要提取一個(gè)公共類
4.是否有更好的實(shí)現(xiàn)
5.錯(cuò)誤是否被更好的處理县钥,而不是粗略屏蔽
6.效率:選用的算法是否更有效率