目录
布局展示要保证16ms 发出vsync信号触发UI渲染一帧。
1.1 Systrace
关注frame
1.2 Layout Inspector
1.3 Choregrapher
获取fps帧数 可以线上使用,具备实时性,api 16之后
private long mStartFrameTime = 0;
private int mFrameCount = 0;
private static final long MONITOR_INTERVAL = 160L; //单次计算FPS使用160毫秒
private static final long MONITOR_INTERVAL_NANOS = MONITOR_INTERVAL * 1000L * 1000L;
private static final long MAX_INTERVAL = 1000L; //设置计算fps的单位时间间隔1000ms,即fps/s;
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private void getFPS() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
return;
}
Choreographer.getInstance().postFrameCallback(new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
if (mStartFrameTime == 0) {
mStartFrameTime = frameTimeNanos;
}
long interval = frameTimeNanos - mStartFrameTime;
if (interval > MONITOR_INTERVAL_NANOS) {
double fps = (((double) (mFrameCount * 1000L * 1000L)) / interval) * MAX_INTERVAL;
mFrameCount = 0;
mStartFrameTime = 0;
} else {
++mFrameCount;
}
Choreographer.getInstance().postFrameCallback(this);
}
});
}
1.4 打开开发者选项–过度绘制
开发者选项里–》调式GPU过度绘制功能,然后回到APP内,如果有过度绘制界面会显示深红色。然后结合其他形式,调式UI 结构。
如设置控件的背景色,又设置了控件的图片,就会引起过度绘制。
1.过多重叠的背景
2.过多叠加的View—如果有叠加的自定义view,可以通过代码指定绘制区域。
canvas.clipRect(mCardLeft,0,mCardLeft+mCardSpacing,mDroidCards.get(i).getHeight());
3.复杂的Layout层级
解决办法:
1.移除Window默认的background:getWidow.setBackgroundDrawable(null); 移除XML布局中非必要的background
2.扁平化视图,减少布局嵌套,减少View数的深度,减少遍历渲染时间,使用约束布局,相对布局等
3.使用ViewStub。
4.使用include+merge
1.5 使用X2C形式加载布局
首先你需要了解布局加载原理:https://blog.csdn.net/chentaishan/article/details/108609347
默认的加载布局的本质其实就是加载XML文件,设计IO的读写,也是属于优化的方向。
1.6 使用AOP获取界面加载时间
execution 监测整个方法内部执行完毕
android.app.Activity.setContentView 监听setContentView方法
@Around("execution(* android.app.Activity.setContentView(..))")
public void getSetContentViewTime(ProceedingJoinPoint joinPoint) {
Signature signature = joinPoint.getSignature();
String name = signature.toShortString();
long time = System.currentTimeMillis();
try {
joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
LogUtils.i(name + " cost " + (System.currentTimeMillis() - time));
}
1.7 获取每个控件的耗时
放到setContentView之前 ,当真正加载布局的时候,会触发Factory2的实现类的拦截逻辑。
LayoutInflaterCompat.setFactory2(getLayoutInflater(), new LayoutInflater.Factory2() {
@Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
//拦截固定组件,打印组件的创建需要的时间,根据时间来得知创建当前view的时长。
if (TextUtils.equals(name, "TextView")) {
// 生成自定义TextView
long time = System.currentTimeMillis();
View view = getDelegate().createView(parent, name, context, attrs);
LogUtils.i(name + " cost " + (System.currentTimeMillis() - time));
}
return view;
}
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
return null;
}
});
1.8 AsyncLayoutInflater 异步加载
常规布局加载有两个问题,读取布局文件慢 :IO 过程 ;反射过程慢 :使用new 创建
1.导入依赖
implementation 'com.android.support:asynclayoutinflater:28.0.0-alpha1'
2.如何使用
new AsyncLayoutInflater(MainActivity.this).inflate(R.layout.activity_main, null, new AsyncLayoutInflater.OnInflateFinishedListener() {
@Override
public void onInflateFinished(@NonNull View view, int i, @Nullable ViewGroup viewGroup) {
setContentView(view);
mRecyclerView = findViewById(R.id.recycler_view);
mRecyclerView.setLayoutManager(new LinearLayoutManager(MainActivity.this));
mRecyclerView.setAdapter(mNewsAdapter);
mNewsAdapter.setOnFeedShowCallBack(MainActivity.this);
}
});
详细查看源码分析