Andorid 布局优化
- 16ms 发出Vsync信号触发UI渲染
- Android设备屏幕刷新频率值:60Hz
工具选择
Systrace
- 关注Frames
- 正常绿色 ,丢帧黄色或者红色
- Alerts栏 会有修改建议
Layout Inspector 查看控件层级
- Android studio工具栏Tools - Layout Inspector
Choreographer 获取fps
- 获取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);
}
});
}
Android 布局加载原理
- 导致布局性能的原因
- 布局文件解析 : IO过程
- 创建View对象 : 反射
- 过度绘制
- LayoutInflater.Factory
- LayoutInflater创建View的一个Hook
- 定制创建View的过程
- Factory2 extend Factory
- onCreateView多了一个parent
获取layout界面布局耗时
AOP方式 - AspectJ
- 切Activity的setContentView
@Around(“execution(*android.app.Activity.setContentView(…))”)
// call 切在方法调用的位置
// execution 切在方法执行的里面
@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));
}
ARTHook实现
获取layout里面单个view的耗时时间
- 在 super.onCreate(savedInstanceState);之前
LayoutInflaterCompat.setFactory2(getLayoutInflater(), new LayoutInflater.Factory2() {
@Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
// 全局更改TextView效果,LayoutInflater是一种Hook
if (TextUtils.equals(name, "TextView")) {
// 生成自定义TextView
}
long time = System.currentTimeMillis();
View view = getDelegate().createView(parent, name, context, attrs); //view控件创建完成
LogUtils.i(name + " cost " + (System.currentTimeMillis() - time));
return view;
}
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
return null;
}
});
super.onCreate(savedInstanceState);
AsyncLayoutInflater 异步Inflat
原因
- xml解析 ,IO流操作
- 反射创建View,比直接new慢3倍
方案
- WorkThred加载布局 ,布局加载完成回调主线程渲染
- implementation ‘com.android.support:asynclayoutinflater:28.0.0-alpha1’
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);
// todo findViewById(id);
TextView tv = findViewById(R.id.tv);
}
});
X2C 优化布局
- View兼容性不好,部分api不存在不推荐使用
- APT编译器翻译xml为java代码
- annotationProcessor ‘com.zhangyue.we:x2c-apt:1.1.2’
- implementation ‘com.zhangyue.we:x2c-lib:1.0.6’
- @Xml(layouts = “activity_main”) 类注解 layout文件名
- X2C.setContentView(MainActivity.this,R.layout.activity_main); 替换 setContentView(view);
视图绘制
- 流程
- 测量
- 布局
- 绘制
- 性能瓶颈
- 每个阶段都耗时
- View层级太多
- 绘制创建对象过多,计算过于耗时等
- 布局层级优化
- 优先选用ConstraintLayout布局
- merge标签使用,只能用于根布局,限制性较大
- 过度绘制优化
- 去掉多余的背景,减少shaoe的使用
- 控件叠加
- 自定义控件开发时, 使用clipRect屏蔽被遮盖的布局
- 开发者选项打开 “调试GPU过度绘制”
- 其它技巧
- Viewstub :占位符,延迟初始化
- 不参与测量,布局,绘制过程
- onDraw(): 避免绘制太大对象
- TextView效率低,可以针对TextVIew优化
- Viewstub :占位符,延迟初始化