Android性能優(yōu)化---內(nèi)存優(yōu)化

讀書筆記:Android應(yīng)用性能優(yōu)化最佳實踐

一、影響卡頓的基本原因

1.繪制任務(wù)太重
2.主線程任務(wù)耗時太長

二、頁面繪制的流程

CPU準備數(shù)據(jù)---GPU從緩存列表獲取數(shù)據(jù)----Display顯示數(shù)據(jù)

三、性能優(yōu)化檢測工具

1.Profile GPU rendering :GPU呈現(xiàn)模式分析

或是使用dumpsys命令更直觀的查看繪制的耗時
adb shell dumpsys gfxinfo com.efrobot.robot.video

2.Systrace UI
分析UI的性能
參考Systrace
3.TraceView
分析函數(shù)的執(zhí)行過程和耗時時間
參考TraceView
4.Hierarchy Viewer
查看Layout的嵌套以及繪制的時間
5.Java Heap
查看內(nèi)存分配
6.Allocation Trace
觀察一段時間內(nèi),內(nèi)存分配的次數(shù)和類
7.Show GPU Overdraw
手機檢查過渡繪制

四、布局優(yōu)化

1.減少層級
合理的使用布局

RelativeLayout:繪制層級少,可以替代LinearLayout多嵌套才能實現(xiàn)的效果,但是性能差,因為要橫向,縱向測量兩次。
LinearLayout:測量少,但是實現(xiàn)復(fù)雜布局需要多次嵌套。

使用Merge標簽減少布局,優(yōu)化布局層級

1.在Activity整體的布局中,跟布局元素要是FrameLayout
2.必須為該布局指定一個ViewGroup,并且attachToRoot為true
3.不能在ViewStub中使用

使用ViewStub提高顯示速度:當布局在加載時不是所有所有元素都要顯示時,可以使用

ViewStub只能加載布局
加載一次后就不能再對布局做操作

避免默認主題的背景:使用activity的自動主題時會有一個默認背景,由DecorView持有

this.getWindow().setBackground(null)

五、啟動耗時優(yōu)化

1.獲取activity的啟動時間
使用ADB命令

adb shell am start -W [packageName]/packageName.ActivityName

2.代碼打點

Application,Activity聲明周期打點
數(shù)據(jù)庫操作打點
其他的耗時業(yè)務(wù)打點
3.優(yōu)化的方向
1.UI布局:檢查是否過渡繪制,布局嵌套,掉幀嚴重
2.邏輯優(yōu)化:異步加載,延期加載,分布加載
3.避免后臺線程操作造成頻繁的GC:例如在ListView滑動時禁止使用后臺線程下載圖片
4.使用TraceView查看updateDisplayListIfDirty方法,最后畫面刷新到屏幕上需要調(diào)用DisplayList方法

六、動畫優(yōu)化

1.使用屬性動畫優(yōu)化刷新率,盡量避免補間動畫
2.在動畫上使用硬件加速(慎重使用 )

七、內(nèi)存優(yōu)化

GC的過程中,任何工作線程都將停止,GC的時間越長,卡頓越明顯,還會造成OOM問題。
1.對象的幾個階段:創(chuàng)建---使用---不可達---不可見--不可達---收集---回收
不可見:無法找到引用該對象的引用關(guān)系,但可能被靜態(tài)變量,JNI層強引用,無法回收

2.內(nèi)存分配
ART和Dalvik虛擬機在內(nèi)存分配
LinearAlloc:存儲虛擬機中的類以及永久數(shù)據(jù),一塊只讀的空間
Zygote Space:
Allocation Space:分配內(nèi)存區(qū)域
Image Space:負責分配預(yù)加載類
Large Space:負責分配大對象地址

3.內(nèi)存回收機制
年輕區(qū)---老年區(qū)--持久區(qū)

新生對象首先在eden區(qū)創(chuàng)建,當eden區(qū)滿時,會將存活的內(nèi)存放到s0區(qū),s0區(qū)滿時,會將此時還活著的對象移動到s1區(qū)域s0清零,當s1區(qū)域也滿時,會復(fù)制s1中的數(shù)據(jù)到s0,反復(fù)執(zhí)行幾次后仍然活著的對象會放到老年代。在老年代經(jīng)理過幾次gc仍然存活的對象會被移動到 持久區(qū)中。

4.使用Allocation Tracker查看內(nèi)存分配
Allocation Tracker可以查看一段時間內(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é)點后,是否還有可達到該對象的路徑,如果有那么表示該對象無法回收,可能出現(xiàn)內(nèi)存泄漏

八、引用區(qū)分

1.強引用:不會被GC回收,回收不了會OOM
2.軟引用:在虛擬機報告內(nèi)存溢出前,如果內(nèi)存不足回收對象
3.弱引用:GC就回收

九、內(nèi)存優(yōu)化

1.在1000數(shù)量級以內(nèi)使用ArrayMap
2.使用基本類型加@IntDef和@StringDef類代替枚舉
3.使用LruCache管理圖片
4.圖片內(nèi)存優(yōu)化

在android上加載圖片,需要將圖片加載成位圖(由多個像素點組成的圖),再解鎖成位圖后,內(nèi)存=寬x高x單位內(nèi)像素。圖片被處理32bit/像素的位圖(ARGB_8888),紅,綠,藍,透明各占8bit。即使圖片不存在透明區(qū)也會分配8bit的透明區(qū)。

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:
如果圖片在內(nèi)存中的大小遠遠大于使用大小可以使用inSampleSize來壓縮圖片,inScaled 會按照現(xiàn)有密度重新劃分目標密度,重新計算圖片大小.

使用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.使用三級緩存對大量圖片進行優(yōu)化

內(nèi)存--本地--網(wǎng)絡(luò)

1.使用LruCache
將最近使用過對象的強引用放到LinkedHashMap中,將最近最少使用的對象移除。
2.內(nèi)存復(fù)用
使用Bitmap的options對象的inBitmap參數(shù):在LruCache移除圖片后,可以將移除的圖片加入到復(fù)用集合中,當需要加載新的圖片時,先查找內(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的值是存儲在常量池(在編譯時期將值保存在.class文件中)中的,一旦創(chuàng)建將不可以修改。

十一、存儲優(yōu)化

1.SharePreference優(yōu)化
Editor的commit是同步寫入,apply是異步寫入。在不需要返回值的情況下,使用apply能夠極大的提升性能
SharePreference的的put和getEditor()會鎖定Editor對象,所以大量寫入SharePreference對象最后提前獲得一個Editor對象,并且通過標志位判斷是否需要讀寫。

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)會自動創(chuàng)建一個事物,如果插入的頻繁就會頻繁的創(chuàng)建事物。所以顯示的創(chuàng)建一次事物,可以提高效率。

getSqliteDB().beginTranscation();
for(){
   getSqliteDB().insert(...)
}
getSqliteDB().setTranscationSuccessful();
getSqliteDB().endTranscation();

十二、Service的包活

1.守護進程
2.網(wǎng)絡(luò)連接保持和Service的交互
3.使用SyncAdapter:是一個系統(tǒng)服務(wù),通過系統(tǒng)的定時器更新數(shù)據(jù)到ContentProvider,工作在一個獨立的進程,屬于核心進程級別。使用它本身也會提高進程級別

十三、耗電優(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.單一職責,一個模塊只負責一件事
2.對象的可擴展開放,可修改關(guān)閉
3.代碼復(fù)用要提取一個公共類
4.是否有更好的實現(xiàn)
5.錯誤是否被更好的處理,而不是粗略屏蔽
6.效率:選用的算法是否更有效率

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容