Github地址:新闻类App (MVP + RxJava + Retrofit+Dagger+ARouter)
绘制原理
- CPU负责计算显示的内容
- GPU负责栅格化(UI元素绘制到屏幕上)
- 16ms发出VSync信号触发UI渲染
- 大多数Android设备屏幕刷新频率:60Hz
优化工具
Systrance
- 关注Framas
- 正常:绿色圆点,丢帧:黄色或者红色
- Alerts栏
Layout Inspector 查看视图层次结构
我的页面结果
image.png
Choreographer
获取fps,线上使用,具备实时性
- API16之后
- Choreographer.getInstance().postFrameCallback
- 代码
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;
private long mStartFrameTime=0;
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;
LogUtils.e(fps);
mFrameCount = 0;
mStartFrameTime = 0;
} else {
++mFrameCount;
}
Choreographer.getInstance().postFrameCallback(this);
}
});
}
获取布局耗时
- 背景:获取每个界面加载耗时
- 实现:覆写方法,手动埋点
- AOP实现,关于AOP的使用大家可以看我之前启动优化这篇文章https://www.jianshu.com/p/6d5cddd56d94
@Around("execution (* android.app.Activity.setContentView**(..))")
public void getSetContentViewTime(ProceedingJoinPoint joinPoint) {
Signature signature = joinPoint.getSignature();
long time = System.currentTimeMillis();
try {
joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
Log.e("SectionAspect", signature.getName() + " cost Time:" + (System.currentTimeMillis() - time));
}
获取某个布局每个控件耗时
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
LayoutInflaterCompat.setFactory2(getLayoutInflater(), new LayoutInflater.Factory2() {
@Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
long time = System.currentTimeMillis();
View view = getDelegate().createView(parent, name, context, attrs);
LogUtils.e(name + " cost " + (System.currentTimeMillis() - time));
return view;
}
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
return null;
}
});
super.onCreate(savedInstanceState);
}
异步Inflate
- 背景介绍
布局文件读取慢,创建View慢(反射:比new慢3倍) - AsyncLayoutInflater实践,只是缓解并不能根本去解决
WorkThread加载布局->回掉主线程 ->节约主线程时间
添加依赖
implementation 'com.android.support:asynclayoutinflater:28.0.0-alpha1'
代码
new AsyncLayoutInflater(this).inflate(R.layout.activity_main, null, new AsyncLayoutInflater.OnInflateFinishedListener() {
@Override
public void onInflateFinished(@NonNull View view, int i, @Nullable ViewGroup viewGroup) {
setContentView(view);
//初始化一些参数比如findViewById等
}
});
- 缺陷:不能设置layoutInflater.Factory,view中不能有依赖主线程的操作
布局加载优化
-
java代码写布局
本质上解决了问题,但是不便于开发,可维护性差 -
X2C框架
保留XML优点,解决其性能问题 -
开发人员写xml,加载Java代码
-
原理:APT编译期间翻译XML为Java代码
-
缺点:部分属性Java不支持,失去了系统的兼容,不能用于线上
依赖
annotationProcessor 'com.zhangyue.we:x2c-apt:1.1.2'
implementation 'com.zhangyue.we:x2c-lib:1.0.6'
代码
@Xml(layouts = "activity_main")
public class MainActivity extends BaseActivity
原本的setContentView-> X2C.setContentView(MainActivity.this,R.layout.activity_main);
视图绘制优化
- 减少View树层级
- 布局宽而浅,避免窄而深
布局的选择
- ConstraintLayout
实现几乎完全扁平化布局
构建复杂布局性能更高
具有RelativeLayout和LinearLayout特性 - FrameLayout能实现的优先使用FrameLayout
- 优先选择RelativeLayout
- 当在不嵌套的情况下,RelativeLayout和LinearLayout同时能满足需求时,优先选择LinearLayout。因为RelativeLayout功能复杂且会出现重复绘制
- 不嵌套使用RelativeLayout
- 不在嵌套linearLayout中使用weight
-
使用include
目的是提高代码的复用性,减少代码,将布局中公共部分抽取供其他layout使用 -
使用merge
解决布局层次级的优化,减少布局嵌套的层次,提高布局加载的效率 -
使用viewStub
ViewStub只有加载该布局的时候才占用资源,INVISIBLE状态是不会绘制出来的。如:加载网络错误的时候显示的布 - onDraw中避免:创建大对象,耗时操作
- TextView优化
过度绘制
- 一个像素最好只被绘制一次
- 调试GPU过度绘制
- 蓝色可以接受
避免过度绘制的方法
- 去掉多余背景色,减少复杂shape使用
- 避免层级叠加
- 自定义view使用clipRect 屏蔽被覆盖View绘制
打开自己的GPU过度绘制
image.png
我的app优化后的效果
image.png
蓝色1x过度绘制
绿色2x过度绘制
淡红色3x过度绘制
红色超过4x过度绘制
网友评论