- 布局
- 繪制
- 啟動(dòng)
- 內(nèi)存
- apk
布局優(yōu)化
布局優(yōu)化的思路就是盡量避免布局文件的層級(jí)铐姚,避免OverDraw
;
Overdraw:
就是過(guò)度繪制俭驮,是指在一幀的時(shí)間內(nèi)(16.67ms)像素被繪制了多次,理論上一個(gè)像素每次只繪制一次是最優(yōu)的,但是由于重疊的布局導(dǎo)致一些像素會(huì)被多次繪制,而每次繪制都會(huì)對(duì)應(yīng)到CPU的一組繪圖命令和GPU的一些操作,當(dāng)這個(gè)操作耗時(shí)超過(guò)16.67ms時(shí)娄帖,就會(huì)出現(xiàn)掉幀現(xiàn)象,也就是我們所說(shuō)的卡頓昙楚,所以對(duì)重疊不可見(jiàn)元素的重復(fù)繪制會(huì)產(chǎn)生額外的開(kāi)銷近速,需要盡量減少Overdraw的發(fā)生
檢測(cè)OverDraw:
- 在開(kāi)發(fā)者選項(xiàng)中,打開(kāi)
調(diào)試GPU過(guò)度繪制(Show GPU OverDraw)
堪旧,會(huì)出現(xiàn)這個(gè)界面:
顏色 | 意義 |
---|---|
沒(méi)有顏色 | 意味著沒(méi)有OverDraw削葱,像素只繪制了一次 |
藍(lán)色 | 意味著overdraw 1倍。像素繪制了兩次淳梦。大片的藍(lán)色還是可以接受的(若整個(gè)窗口是藍(lán)色的佩耳,可以擺脫一層) |
綠色 | 意味著overdraw 2倍。像素繪制了三次谭跨。中等大小的綠色區(qū)域是可以接受的但你應(yīng)該嘗試優(yōu)化干厚、減少它們李滴。 |
淺紅 | 意味著overdraw 3倍。像素繪制了四次蛮瞄,小范圍可以接受所坯。 |
暗紅 | 意味著overdraw 4倍。像素繪制了五次或者更多挂捅。這是錯(cuò)誤的芹助,要修復(fù)它們。 |
如何優(yōu)化:
1. 合理布局xml:
- 在編寫xml界面時(shí)闲先,刪除無(wú)用的控件和層次状土;
- 其次如果一個(gè)界面LinearLayout和RelativeLayout都可以實(shí)現(xiàn),采用LinearLayout伺糠,因?yàn)镽elativeLayout的內(nèi)部實(shí)現(xiàn)較復(fù)雜蒙谓,布局過(guò)程會(huì)花費(fèi)更多的CPU事件;
2. 使用include训桶,merge累驮,ViewStub
- 使用include標(biāo)簽復(fù)用布局文件
- 使用merge標(biāo)簽配合include減少布局的層級(jí),如果被復(fù)用的布局文件有一層布局是不需要的,可以使用merge去掉多余的一層
- ViewStub繼承View,它本身不參與任何的布局和繪制過(guò)程堡牡,作用于按需加載所需的布局文件;調(diào)用
setVisibility()和inflate()
后ViewStub才會(huì)生效置侍;
3. 使用ClipRect & QuickReject
為了解決Overdraw的問(wèn)題,Android系統(tǒng)會(huì)通過(guò)避免繪制那些完全不可見(jiàn)的組件來(lái)盡量減少消耗拦焚。但是不幸的是蜡坊,對(duì)于那些過(guò)于復(fù)雜的自定義的View(通常重寫了onDraw方法),Android系統(tǒng)無(wú)法檢測(cè)在onDraw里面具體會(huì)執(zhí)行什么操作耕漱,系統(tǒng)無(wú)法監(jiān)控并自動(dòng)優(yōu)化算色,也就無(wú)法避免Overdraw了抬伺。但是我們可以通過(guò)canvas.clipRect()來(lái)幫助系統(tǒng)識(shí)別那些可見(jiàn)的區(qū)域螟够。這個(gè)方法可以指定一塊矩形區(qū)域,只有在這個(gè)區(qū)域內(nèi)才會(huì)被繪制峡钓,其他的區(qū)域會(huì)被忽視妓笙。這個(gè)API可以很好的幫助那些有多組重疊組件的自定義View來(lái)控制顯示的區(qū)域。同時(shí)clipRect方法還可以幫助節(jié)約CPU與GPU資源能岩,在clipRect區(qū)域之外的繪制指令都不會(huì)被執(zhí)行寞宫,那些部分內(nèi)容在矩形區(qū)域內(nèi)的組件,仍然會(huì)得到繪制拉鹃。除了clipRect方法之外辈赋,我們還可以使用canvas.quickreject()來(lái)判斷是否沒(méi)和某個(gè)矩形相交鲫忍,從而跳過(guò)那些非矩形區(qū)域內(nèi)的繪制操作
Android畫布剪裁函數(shù)clipRect詳解
使用clipRect()優(yōu)化OverDraw
其實(shí)clipRect函數(shù)就是通過(guò)巧妙的剪裁和拼接畫布,將自定義控件中重疊的部分钥屈,做優(yōu)化處理悟民。
繪制優(yōu)化
繪制優(yōu)化指View的onDraw()過(guò)程要避免大量操作,主要體現(xiàn)在兩個(gè)方面:
- 不能創(chuàng)建新的局部對(duì)象
- 不能做耗時(shí)操作
因?yàn)閛nDraw過(guò)程是會(huì)被頻繁調(diào)用的篷就,大量的局部變量會(huì)占用過(guò)多的內(nèi)存射亏,并且會(huì)引發(fā)頻繁的GC,GC會(huì)影響程序的執(zhí)行效率(Stop the World);過(guò)多的耗時(shí)操作會(huì)十分消耗CPU的時(shí)間片竭业,導(dǎo)致View繪制的不流暢智润;官方規(guī)定View的繪制率保證在60fps最好,要求每幀的時(shí)間不超過(guò)16ms(1000/60)未辆,盡量降低onDraw方法的復(fù)雜度會(huì)有效的提高程序的效率窟绷;
啟動(dòng)優(yōu)化
啟動(dòng)的分類
- 冷啟動(dòng):當(dāng)啟動(dòng)應(yīng)用時(shí),后臺(tái)沒(méi)有該應(yīng)用的進(jìn)程鼎姐,這時(shí)系統(tǒng)會(huì)首先會(huì)創(chuàng)建一個(gè)新的進(jìn)程分配給該應(yīng)用钾麸,這種啟動(dòng)方式就是冷啟動(dòng)
- 熱啟動(dòng):當(dāng)啟動(dòng)應(yīng)用時(shí),后臺(tái)已有該應(yīng)用的進(jìn)程炕桨,比如按下home鍵饭尝,這種在已有進(jìn)程的情況下,這種啟動(dòng)會(huì)從已有的進(jìn)程中來(lái)啟動(dòng)應(yīng)用献宫,這種啟動(dòng)方式叫熱啟動(dòng)
- 溫啟動(dòng):當(dāng)啟動(dòng)應(yīng)用時(shí)钥平,后臺(tái)已有該應(yīng)用的進(jìn)程,但是啟動(dòng)的入口Activity被干掉了姊途,比如按了back鍵涉瘾,應(yīng)用雖然退出了,但是該應(yīng)用的進(jìn)程是依然會(huì)保留在后臺(tái)捷兰,這種啟動(dòng)方式叫溫啟動(dòng)
啟動(dòng)時(shí)間的檢測(cè):
1.ADB命令: adb shell am start -W packagename/MainActivity
參數(shù) | 含義 |
---|---|
ThisTime | 最后一個(gè)啟動(dòng)的Activity的啟動(dòng)耗時(shí)立叛; |
TotalTime | 新應(yīng)用啟動(dòng)的耗時(shí),包括新進(jìn)程的啟動(dòng)和Activity的啟動(dòng) |
WaitTime | ActivityManagerService啟動(dòng)App的Activity時(shí)的總時(shí)間(包括當(dāng)前Activity的onPause()和自己Activity的啟動(dòng)) |
冷啟動(dòng)白屏出現(xiàn)的原因:
ActivityStack的startActivityLock()會(huì)調(diào)用showStartingWindow() 添加一個(gè)空白的window贡茅;
處理冷啟動(dòng)白屏的方法:
-
通過(guò)Theme設(shè)置window不顯示或者window設(shè)置為透明的
為Theme設(shè)置背景圖
推薦第二種秘蛇,第一種如果根activity啟動(dòng)耗時(shí)的話會(huì)出現(xiàn)卡頓現(xiàn)象;
啟動(dòng)時(shí)的其他優(yōu)化
- 多進(jìn)程Application重復(fù)創(chuàng)建顶考,
動(dòng)態(tài)判斷包名避免重復(fù)初始化
- 大量子線程任務(wù),
設(shè)置工作線程的優(yōu)先級(jí)
- 線程池的開(kāi)銷比單一線程大很多赁还,
在啟動(dòng)的時(shí)候使用線程更優(yōu)
- 信息未緩存,重復(fù)獲取影響性能
內(nèi)存優(yōu)化
- 內(nèi)存抖動(dòng):短時(shí)間內(nèi)大量的對(duì)象創(chuàng)建和銷毀會(huì)出現(xiàn)內(nèi)存抖動(dòng)
- 內(nèi)存泄漏:對(duì)象內(nèi)存由于某些原因無(wú)法釋放和回收
- 內(nèi)存溢出:大量的內(nèi)存泄漏會(huì)導(dǎo)致內(nèi)存溢出OOM
-
內(nèi)存卡頓:安卓的垃圾收集器是CMS驹沿,CMS的
Stop the World
會(huì)造成卡頓
常見(jiàn)的字符串拼接會(huì)造成內(nèi)存的抖動(dòng):
因?yàn)樽址莊inal常量艘策,一經(jīng)賦值就無(wú)法改變,但是java語(yǔ)言對(duì)
+
進(jìn)行了語(yǔ)法糖設(shè)置渊季,實(shí)際是將String轉(zhuǎn)化為StringBuffer朋蔫,每次都要new 一個(gè)StringBuffer出來(lái)罚渐;所以我們?nèi)绻苯邮褂肧tringBuffer的append()就可以避免重復(fù)創(chuàng)建StringBuffer對(duì)象;
預(yù)防內(nèi)存抖動(dòng):
- 避免在循環(huán)中創(chuàng)建對(duì)象
- 避免在重復(fù)調(diào)用的方法中創(chuàng)建局部對(duì)象
- 使用對(duì)象池:Handler的Message的obtain()
內(nèi)存的檢測(cè)
-
AS自帶的Profiler
內(nèi)存泄漏的原因:長(zhǎng)生命周期對(duì)象持有短生命周期對(duì)象引用驯妄,導(dǎo)致短生命周期對(duì)象無(wú)法回收
常見(jiàn)的內(nèi)存泄漏:
- 單例持有activity的引用
- 外部類持有內(nèi)部類的引用(Hnadler搅轿,Runnable,Listener)
非靜態(tài)內(nèi)部類和匿名內(nèi)部類會(huì)默認(rèn)持有外部類的引用
- 大量的強(qiáng)引用
內(nèi)存泄漏的檢測(cè):
- leakCanary
避免內(nèi)存溢出OOM的措施:
- 使用靜態(tài)內(nèi)部類
- 使用WeakReference
- 增加JVM啟動(dòng)參數(shù)的內(nèi)存大小
- 注意資源的手動(dòng)回收(關(guān)閉流富玷,停止動(dòng)畫璧坟,注銷EventBus,刪除元素置null)
Bitmap優(yōu)化
APK瘦身
APK的結(jié)構(gòu):
目錄:
- assets:存放應(yīng)用的資源赎懦,這些資源能夠通過(guò)AssetManager對(duì)象獲得
- lib:包含了針對(duì)處理器層面的被編譯的代碼雀鹃。這個(gè)目錄針對(duì)每個(gè)平臺(tái)類型都有一個(gè)子目錄,比如armeabi, armeabi-v7a, arm64-v8a, x86, x86_64和mips励两。
- res:包含了沒(méi)被編譯到resources.arsc的資源黎茎。
- META-INF:包含CERT.SF和CERT.RSA簽名文件,也包含了MANIFEST.MF文件当悔。(譯注:校驗(yàn)這個(gè)APK是否被人改動(dòng)過(guò))
文件:
- classes.dex:包含了能被Dalvik/Art虛擬機(jī)理解的 dex 文件格式的類傅瞻。
- resources.arsc:包含了被編譯的資源。該文件包含了res/values目錄的所有配置的 xml 內(nèi)容盲憎。打包工具將 xml 內(nèi)容編譯成二進(jìn)制形式并壓縮嗅骄。這些內(nèi)容包含了語(yǔ)言字符串和styles,還包含了那些內(nèi)容雖然不直接存儲(chǔ)在resources.arsc文件中饼疙,但是給定了該內(nèi)容的路徑溺森,比如布局文件和圖片。所以又叫 資源映射表
- AndroidManifest.xml:包含了主要的Android配置文件窑眯。這個(gè)文件列出了應(yīng)用名稱屏积、版本、訪問(wèn)權(quán)限磅甩、引用的庫(kù)文件炊林。該文件使用二進(jìn)制 xml 格式存儲(chǔ)。(譯注:該文件還能看到應(yīng)用的minSdkVersion, targetSdkVersion等信息)
瘦身步驟:
1. 使用Lint
檢測(cè)res目錄卷要,刪除無(wú)用資源
2. 壓縮圖片渣聚,或者使用svg
,Webp
,tinyPNG
3. 修改lib配置:
安卓系統(tǒng)現(xiàn)在支持7中CPU架構(gòu),每種架構(gòu)都支持一種ABI(二進(jìn)制接口却妨,Android binary interface)饵逐,每種ABI都定義了二進(jìn)制文件(.so文件)括眠,我們可以刪除其他的CPU架構(gòu)的適配彪标,只保留armeabi-v7a
一種主流架構(gòu);
ndk {
//設(shè)置支持的so庫(kù)架構(gòu)
abiFilters "armeabi-v7a"
}