新闻类App (MVP + RxJava + Retrofit+Dagger+ARouter)性能优化之App布局优化

Github地址:新闻类App (MVP + RxJava + Retrofit+Dagger+ARouter)

绘制原理

  • CPU负责计算显示的内容
  • GPU负责栅格化(UI元素绘制到屏幕上)
  • 16ms发出VSync信号触发UI渲染
  • 大多数Android设备屏幕刷新频率:60Hz

优化工具

Systrance

  • 关注Framas
  • 正常:绿色圆点,丢帧:黄色或者红色
  • Alerts栏

Layout Inspector 查看视图层次结构

image.png

我的页面结果
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过度绘制

你可能感兴趣的:(新闻类App (MVP + RxJava + Retrofit+Dagger+ARouter)性能优化之App布局优化)