FPS : 1s 內(nèi) SurfaceFLinger 提交到屏幕的幀數(shù)。
FPS 很低,但是不卡。 是因?yàn)槟愕?app 在這 1s 內(nèi)只有 30 幀的顯示需求; 如果沒有繪制需求, FPS 就是 0;
App 停止操作后 FPS 還在變化, 是因?yàn)槊恳粠暮铣墒轻槍κ謾C(jī)所有進(jìn)程的, 即使你的 app 停止了繪制,其他的進(jìn)程可能還在繪制,例如通知欄;
如何評測流暢度?
FPS 評測流暢度并不準(zhǔn)確,那么應(yīng)該找到一個什么樣的指標(biāo)進(jìn)行評測呢?
Android 4.1 引入了 VSync 機(jī)制。
VSync 機(jī)制就像一臺轉(zhuǎn)速固定的發(fā)動機(jī)(60轉(zhuǎn)/s),每一轉(zhuǎn)帶動著做一些 UI 相關(guān)的事情。有時候因?yàn)楦鞣N阻力, 某一圈的工作量比較重, 超過了 16.6ms, 那么這一秒內(nèi)就不是 60 轉(zhuǎn)了。
我們通過測量這個轉(zhuǎn)速,來評判應(yīng)用的流暢度
Android 系統(tǒng)中, Choreographer 類通過監(jiān)聽系統(tǒng) VSync 信號來協(xié)調(diào) App 的動畫、輸入、繪制工作。 它本身是單例模式。
丟幀計(jì)算:在一次 Loop 時,如果執(zhí)行時間超過了 16.6ms, 那么多余 16.6ms 的時間除以 16.6ms, 即是當(dāng)前 App 的丟幀情況。
注冊 Choreographer callback 事件, 在每一幀繪制完成后, 執(zhí)行回調(diào)
Choreographer.getInstance().postFrameCallback(new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
//do calculate
Choreographer.getInstance().postFrameCallback(this);
}
});
流暢度值計(jì)算:
SM = 幀率(60) * (單位時長總幀數(shù) - 單位時長丟幀數(shù)) / 單位時長總幀數(shù)
評測標(biāo)準(zhǔn):
SM值 | 卡頓情況 |
---|---|
SM <= 20 | 卡死 |
20 < SM <= 40 | 很卡 |
40 < SM <= 50 | 較卡 |
50 < SM <= 60 | 流暢 |
如何優(yōu)化
避免過度繪制
設(shè)置中開發(fā)者選項(xiàng)打開過度繪制區(qū)域查看。
OverDraw 倍數(shù) | 像素點(diǎn)繪制次數(shù) | 可接受區(qū)域 | |
---|---|---|---|
無色 | 0X | 1 | 全局 |
藍(lán)色 | 1X | 2 | 大片 |
綠色 | 2X | 3 | 中等 |
淺紅 | 3X | 4 | 小,少 |
暗紅 | 4X | ≥ 5 | 無 |
解決 UI 布局中不合理的地方
- 減少布局層次, 利用 <merge> <ViewStub> 消除無用的父布局
- 不常用的 UI 設(shè)置成 GONE
- 靈活使用 ConstraintLayout
Lint 掃描
優(yōu)化邏輯
- TraceView 尋找卡住主線程的地方
- Systrace 獲取 app 運(yùn)行是線程的信息以及 API 的執(zhí)行情況
- 避免在主線程執(zhí)行 IO 操作
- 優(yōu)化布局結(jié)構(gòu)
- 盡量多使用 RelativeLayout 和 LinearLayout
- 在布局層次一樣時,LinearLayout 比 RelativeLayout 性能稍高一點(diǎn)
- 相對復(fù)雜的而不懼, RelativeLayout 可以比 LinearLayout 減少層級
- 可復(fù)用的組件抽取處理啊通過 include 標(biāo)簽使用
- 使用 ViewStub 標(biāo)簽來加載一些不常用的布局
- 使用 merge 標(biāo)簽減少布局的嵌套層次
- 去掉多余的背景顏色,減少過度繪制
- 盡量多使用 RelativeLayout 和 LinearLayout
Github 上找到的一個 SM 庫實(shí)現(xiàn):
https://github.com/friendlyrobotnyc/TinyDancer