NimbleDroid 是美國哥倫比亞大學(xué)的博士創(chuàng)業(yè)團(tuán)隊(duì)研發(fā)出來的分析Android app性能指標(biāo)的系統(tǒng),分析的方式有靜態(tài)和動態(tài)兩種方式,
流暢度優(yōu)化
顯示原理
- cpu計(jì)算: measure layout draw => displayList
- gpu柵格化: displayList => 位圖
- 屏幕顯示: 位圖 => 顯示 60hz = 1000/60 = 16.67ms
檢測流暢度的幾種方式
- 騰訊GT https://github.com/Tencent/GT
- BlockCanary https://github.com/seiginonakama/BlockCanaryEx
- 開啟GPU渲染模式 水平的綠色橫線是 16ms android 6.0以上顏色多種
- Swap Buffers:表示處理任務(wù)的時間,也可以說是CPU等待GPU完成任務(wù)的時間,線條越高,表示GPU做的事情越多;
- Command Issue:表示執(zhí)行任務(wù)的時間,這部分主要是Android進(jìn)行2D渲染顯示列表的時間,為了將內(nèi)容繪制到屏幕上,Android需要使用Open GL ES的API接口來繪制顯示列表,紅色線條越高表示需要繪制的視圖更多;
- Sync & Upload:表示的是準(zhǔn)備當(dāng)前界面上有待繪制的圖片所耗費(fèi)的時間,為了減少該段區(qū)域的執(zhí)行時間,我們可以減少屏幕上的圖片數(shù)量或者是縮小圖片的大小;
- Draw:表示測量和繪制視圖列表所需要的時間,藍(lán)色線條越高表示每一幀需要更新很多視圖,或者View的onDraw方法中做了耗時操作;
- 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 Time/Vsync Delay:表示在主線程執(zhí)行了太多的任務(wù),導(dǎo)致UI渲染跟不上vSync的信號而出現(xiàn)掉幀的情況; 是重點(diǎn)優(yōu)化對象 配合cpu profile 看看耗時任務(wù)
StrictMode 可檢測耗時
-
過度繪制問題:一個像素點(diǎn)被繪制多次
image.png androidStudio 的布局分析器 查看布局結(jié)構(gòu)
第三方布局分析器 https://github.com/romainguy/ViewServer
布局嵌套太深導(dǎo)致 measure 計(jì)算太多 盡量減少布局嵌套 少用 wrap_content
流暢度優(yōu)化手段:
- 避免過度重繪,移除不必要的背景,降低布局嵌套層數(shù) 和 wrap_content 減少measure次數(shù)
可使用 約束布局 減少 組合嵌套 線性布局和相對布局, 可使用 include merge等等 還可以用viewStub延遲初始化一部分view 第三方工具x2c 把xml布局 轉(zhuǎn)換為代碼實(shí)現(xiàn), 減少讀取布局文件占用的時間- 避免主線程耗時 (gpu模式 深綠色),比如與視圖有關(guān)的 model 要在io線程處理好 再在主線程渲染
- 大殺招:用IdleHandler 進(jìn)行預(yù)處理
- 避免頻繁gc 一般來說瞬間大量產(chǎn)生對象一般是因?yàn)槲覀冊诖a的循環(huán)中new對象, 或是在onDraw中創(chuàng)建對象等. 所以說這些地方是我們尤其需要注意的...
- 刷新的時候局部刷新
網(wǎng)絡(luò)優(yōu)化
1.抓包工具Wireshark, Fiddler, Charles
2.解決方案
- 減少Radio(頻射模塊)活躍時間
- 也就是減少網(wǎng)絡(luò)數(shù)據(jù)獲取的頻次.
- 這就減少了radio的電量消耗, 控制電量使用.
- 減少獲取數(shù)據(jù)包的大小
- 可以減少流量消耗
- 也可以讓每次請求更快, 在網(wǎng)絡(luò)情況不好的情況下也有良好表現(xiàn), 提升用戶體驗(yàn).
圖片上傳與展示 實(shí)戰(zhàn)
a.api網(wǎng)關(guān),避免一個頁面的數(shù)據(jù)來自多個接口
b.壓縮
c.網(wǎng)絡(luò)緩存
d.弱網(wǎng)優(yōu)化
e.不適用域名 使用ip 繞過dns解析步驟
圖片上傳 和 圖片查看器
- 采用魯班壓縮 和gzip 對上傳的圖片和數(shù)據(jù)進(jìn)行壓縮
- 顯示時采用縮略圖策略,魔圖規(guī)則
- 減少不必要的網(wǎng)絡(luò)請求,而采用客戶端的基本邏輯去改變頁面 例如點(diǎn)贊按鈕后 直接本地+1
- 打包網(wǎng)絡(luò)請求
- 動態(tài)超時時間
- wifi下與拉取網(wǎng)絡(luò)數(shù)據(jù) 例如網(wǎng)易的離線新聞 或者 離線更新包
- 本地緩存
- http緩存
- 控制網(wǎng)絡(luò)請求頻率
- 網(wǎng)絡(luò)請求 接口降級
- 重試機(jī)制 重試次數(shù)
- 弱網(wǎng) 無圖模式
- ip直連 避免dns 或者使用 httpdns
- webp圖片 能省1/3
- http2 二進(jìn)制多路復(fù)用
- 避免輪詢 采用長連接
電量優(yōu)化
檢測工具 energy profile
用adb命令 Batterystats 導(dǎo)出 電量記錄文件
然后用 谷歌開源的 batteryHistorian 導(dǎo)入記錄文件 分析電量情況
- 和網(wǎng)絡(luò)優(yōu)化息息相關(guān),因?yàn)榫W(wǎng)絡(luò)非常耗電量,做好網(wǎng)絡(luò)優(yōu)化 手機(jī)的通過內(nèi)置的射頻模塊和基站 而這個射頻模塊(radio)是非常耗電的.
- 及時釋放wakeLock (保持屏幕常亮)
- 合理釋放GPS的監(jiān)聽
- BatteryManager 監(jiān)聽充電狀態(tài),一些耗電操作可以在充電情況去做
- 定時任務(wù) 可以做暫停 例如 輪播圖 不可見時 要及時停止
啟動優(yōu)化
冷啟動:直接啟動
熱啟動:退到后臺后進(jìn)程沒死,activity沒死
溫啟動:退到后臺進(jìn)程沒死,但activity被回收
檢測工具 GT
app啟動優(yōu)化
- 將application oncreate中的方法 拆分成 同步和異步, 并且延遲初始化一部分 加入一個
- windowBackground 作為啟動頁背景 首屏盡量簡單 可以寫一個什么都不做的activity 只顧跳轉(zhuǎn), 不做setcontentview
- 善用分析工具
- 優(yōu)化包體積可以加速啟動,優(yōu)化dex分包可以加速啟動
- 這里可以說下方舟編譯器的原理
安卓系統(tǒng)中也有這樣的編譯器,目前有兩個編譯器,分別是Art和Jit,Art是在你首次安裝APP時,對大約20%的核心程序代碼翻譯成0、1這樣的機(jī)器語言保存在手機(jī)中,另外的80%非核心代碼則在運(yùn)行時用Jit編譯器來翻譯,所以才有了華為所說的連執(zhí)行連翻譯,自然效率不高了。
而如果APP通過方舟編譯器開發(fā),打包之后的APP就直接以0、1這樣的機(jī)器碼存在,這樣安裝到手機(jī)之中后,就是0、1這樣的機(jī)器碼,不存在翻譯過程,機(jī)器就直接可以執(zhí)行,不需要轉(zhuǎn)換,自然效率更高,體現(xiàn)在操作上自然就是啟動更快,操作更流暢了
https://baijiahao.baidu.com/s?id=1631785697366805454&wfr=spider&for=pc
- 把一些方法抽取出來. 在頁面繪制后, 等到UI線程空閑的時候, 再去執(zhí)行這些耗時方法.idlehandler
包體積優(yōu)化
- 圖片資源壓縮 tinypng ,也可使用webp 有損壓縮, 支持透明通道
- 盡量用 xml shape 替代切圖,或者使用.9png 替代大圖
- release去除多余so,保留 arm-v7
- 只保留一xxhdpi的切圖 可使用字節(jié)跳動的方案 解決適配問題
原理 px = dp * density 去根據(jù)寬度修改 density,保證 寫死dp的和寬度的比例 去適配不同的寬度 - 方舟編譯器
- 混淆
- 去除無用資源 ,去除用的語言支持
defaultConfig {
resConfigs "zh"
}
buildTypes {
release {
shrinkResources true
}
}
- 大殺器 插件化
- gradle provided ,根據(jù)不同渠道,可能有不同的功能, 使用provider,會讓你通過編譯,但不會加入到apk
- redex 是 Facebook 開源的一款字節(jié)碼優(yōu)化工具
- 減少gc次數(shù)
內(nèi)存優(yōu)化
檢測工具:leakCanary mat
避免頻繁gc 每次gcandroid系統(tǒng)可能會凍結(jié)200ms
避免內(nèi)存占用過多
- 使用 高效的數(shù)據(jù)結(jié)構(gòu) SparseArray 不是線程安全的
sparseArray<E> 替代 hashmap<Integer,E>
sparseBooleanArray 替代 hashmap<Integer,Boolean
sparseIntArray替代 hashmap<Integer,Integer>- 大圖加載 使用 瓦片的形式 加載 顯示的部分
- Lru 過期內(nèi)存中的對象,防止長期占用
- 內(nèi)存告警的時候及時釋放對象,(圖片)
- 避免使用線程池自帶的四種構(gòu)造方式 可能oom
避免內(nèi)存泄漏
- context被靜態(tài)變量持有
- context被單例持有
- context被屬性動畫持有, ondestory 要cancel動畫
- context1被內(nèi)部類持有 被handler持有, 要使用靜態(tài)內(nèi)部類
- context被application持有
- 資源沒關(guān)閉造成內(nèi)存泄露 bitmap
總結(jié)
1、對于生命周期比Activity長的對象如果需要應(yīng)該使用ApplicationContext,在需要使用Context參數(shù)的時候先考慮Application.Context.
2、在引用組件Activity,F(xiàn)ragment時,優(yōu)先考慮使用弱引用。
3、在使用異步操作時注意Activity銷毀時,需要清空任務(wù)列表,如果有使用集合,將集合清空并置空,釋放相應(yīng)的資源。
4、內(nèi)部類持有外部類的引用盡量修改成靜態(tài)內(nèi)部類中使用弱引用持有外部類的引用。
5、 留意活動的生命周期,在使用單例,靜態(tài)對象,全局性集合的時候應(yīng)該特別注意置空。