讀書筆記:Android應用性能優化最佳實踐
一、影響卡頓的基本原因
1.繪制任務太重
2.主線程任務耗時太長
二、頁面繪制的流程
CPU準備數據---GPU從緩存列表獲取數據----Display顯示數據
三、性能優化檢測工具
1.Profile GPU rendering :GPU呈現模式分析
或是使用dumpsys命令更直觀的查看繪制的耗時
adb shell dumpsys gfxinfo com.efrobot.robot.video
2.Systrace UI
分析UI的性能
參考Systrace
3.TraceView
分析函數的執行過程和耗時時間
參考TraceView
4.Hierarchy Viewer
查看Layout的嵌套以及繪制的時間
5.Java Heap
查看內存分配
6.Allocation Trace
觀察一段時間內,內存分配的次數和類
7.Show GPU Overdraw
手機檢查過渡繪制
四、布局優化
1.減少層級
合理的使用布局
RelativeLayout:繪制層級少,可以替代LinearLayout多嵌套才能實現的效果,但是性能差,因為要橫向,縱向測量兩次。
LinearLayout:測量少,但是實現復雜布局需要多次嵌套。
使用Merge標簽減少布局,優化布局層級
1.在Activity整體的布局中,跟布局元素要是FrameLayout
2.必須為該布局指定一個ViewGroup,并且attachToRoot為true
3.不能在ViewStub中使用
使用ViewStub提高顯示速度:當布局在加載時不是所有所有元素都要顯示時,可以使用
ViewStub只能加載布局
加載一次后就不能再對布局做操作
避免默認主題的背景:使用activity的自動主題時會有一個默認背景,由DecorView持有
this.getWindow().setBackground(null)
五、啟動耗時優化
1.獲取activity的啟動時間
使用ADB命令
adb shell am start -W [packageName]/packageName.ActivityName
2.代碼打點
Application,Activity聲明周期打點
數據庫操作打點
其他的耗時業務打點
3.優化的方向
1.UI布局:檢查是否過渡繪制,布局嵌套,掉幀嚴重
2.邏輯優化:異步加載,延期加載,分布加載
3.避免后臺線程操作造成頻繁的GC:例如在ListView滑動時禁止使用后臺線程下載圖片
4.使用TraceView查看updateDisplayListIfDirty方法,最后畫面刷新到屏幕上需要調用DisplayList方法
六、動畫優化
1.使用屬性動畫優化刷新率,盡量避免補間動畫
2.在動畫上使用硬件加速(慎重使用 )
七、內存優化
GC的過程中,任何工作線程都將停止,GC的時間越長,卡頓越明顯,還會造成OOM問題。
1.對象的幾個階段:創建---使用---不可達---不可見--不可達---收集---回收
不可見:無法找到引用該對象的引用關系,但可能被靜態變量,JNI層強引用,無法回收
2.內存分配
ART和Dalvik虛擬機在內存分配
LinearAlloc:存儲虛擬機中的類以及永久數據,一塊只讀的空間
Zygote Space:
Allocation Space:分配內存區域
Image Space:負責分配預加載類
Large Space:負責分配大對象地址
3.內存回收機制
年輕區---老年區--持久區
新生對象首先在eden區創建,當eden區滿時,會將存活的內存放到s0區,s0區滿時,會將此時還活著的對象移動到s1區域s0清零,當s1區域也滿時,會復制s1中的數據到s0,反復執行幾次后仍然活著的對象會放到老年代。在老年代經理過幾次gc仍然存活的對象會被移動到 持久區中。
4.使用Allocation Tracker查看內存分配
Allocation Tracker可以查看一段時間內內存分配的次數,和分配的大小,詳見參考文章
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,查看合并節點后,是否還有可達到該對象的路徑,如果有那么表示該對象無法回收,可能出現內存泄漏
八、引用區分
1.強引用:不會被GC回收,回收不了會OOM
2.軟引用:在虛擬機報告內存溢出前,如果內存不足回收對象
3.弱引用:GC就回收
九、內存優化
1.在1000數量級以內使用ArrayMap
2.使用基本類型加@IntDef和@StringDef類代替枚舉
3.使用LruCache管理圖片
4.圖片內存優化
在android上加載圖片,需要將圖片加載成位圖(由多個像素點組成的圖),再解鎖成位圖后,內存=寬x高x單位內像素。圖片被處理32bit/像素的位圖(ARGB_8888),紅,綠,藍,透明各占8bit。即使圖片不存在透明區也會分配8bit的透明區。
5.可以修改圖片的格式
RGB_565(16bit),ARGB_4444(16bit),ALPHA_8(8bit)。小屏手機且對圖像要求不高可以使用rgb,圓角頭像可以使用ARGB4444,.
BitmapFactory.Options option =new BitmapFactory.Options()
options.inPreferredConfig=Bitmap.Config.RGB_565
BitmapFactory.decodeStream(is,null,options)
6.inSampleSize,inDensity,inTargetDensity:
如果圖片在內存中的大小遠遠大于使用大小可以使用inSampleSize來壓縮圖片,inScaled 會按照現有密度重新劃分目標密度,重新計算圖片大小.
使用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.使用三級緩存對大量圖片進行優化
內存--本地--網絡
1.使用LruCache
將最近使用過對象的強引用放到LinkedHashMap中,將最近最少使用的對象移除。
2.內存復用
使用Bitmap的options對象的inBitmap參數:在LruCache移除圖片后,可以將移除的圖片加入到復用集合中,當需要加載新的圖片時,先查找內存,再查找復用集合中是否有符合的圖片。
Bitmap inBitmap=cache.getBitmapFromReusableSet()
if(inBitmap!=null){
options.inBitmap=inBitmap;
}
3.使用磁盤緩存
使用DiskLruCache
十、String的優化
參考:http://www.androidchina.net/5940.html
String的值是存儲在常量池(在編譯時期將值保存在.class文件中)中的,一旦創建將不可以修改。
十一、存儲優化
1.SharePreference優化
Editor的commit是同步寫入,apply是異步寫入。在不需要返回值的情況下,使用apply能夠極大的提升性能
SharePreference的的put和getEditor()會鎖定Editor對象,所以大量寫入SharePreference對象最后提前獲得一個Editor對象,并且通過標志位判斷是否需要讀寫。
if(開關沒有改變&&沒有發生變化){
}
2.數據庫優化
1.使用SQLiteStatement來執行插入操作
SQLiteStatement statement=getSqliteDB().compileStatement(STR_INSERT_STATEMENT_CONTACTS);
statement.clearBindings();
statement.bind..();
2.更好的方法是使用事物
在數據庫插入代碼中,如果沒有顯示的使用事物,系統會自動創建一個事物,如果插入的頻繁就會頻繁的創建事物。所以顯示的創建一次事物,可以提高效率。
getSqliteDB().beginTranscation();
for(){
getSqliteDB().insert(...)
}
getSqliteDB().setTranscationSuccessful();
getSqliteDB().endTranscation();
十二、Service的包活
1.守護進程
2.網絡連接保持和Service的交互
3.使用SyncAdapter:是一個系統服務,通過系統的定時器更新數據到ContentProvider,工作在一個獨立的進程,屬于核心進程級別。使用它本身也會提高進程級別
十三、耗電優化
1.android5.0提供了Battery Historian查看電量使用
2.使用命令
//獲取電量權限,并重置電量日志
adb shell dumpsys batterystats --enable full-wake-history
shell dumpsys batterystats --reset
//保存數據
adb bugreport >bugreport.txt
將文件轉化為html,需要下載battery-historian
python historian.py -a bugreport.txt >battery.html
3.注意消耗:使用WakeLock一定要記得釋放
4.使用JobScheduler執行部分任務
重要不緊急的任務
耗電量較大的任務
可以批量執行的任務
十二、代碼審查
1.單一職責,一個模塊只負責一件事
2.對象的可擴展開放,可修改關閉
3.代碼復用要提取一個公共類
4.是否有更好的實現
5.錯誤是否被更好的處理,而不是粗略屏蔽
6.效率:選用的算法是否更有效率