布局优化

布局优化

相关参考:https://androidperformance.com/2019/07/27/Android-Hardware-Layer/

优化工具

  • Systrace
    • 关注 Frames 行:正常绿色,丢帧黄色或红色
    • 关注 Alerts 栏:自动分析性能问题的一些条目
  • Layout Inspector
    • Android Studio 自带工具,查看视图层次结构
  • Choreographer
  • uiautomatorviewer
    • SDK 自带布局查看工具:<SDK>/tools/bin/uiautomatorviewer.bat
  • adb 命令测试:

Android 布局加载原理

  1. setContentView:调用 LayoutInflater.inflate
  2. Resources.getLayout:解析 xml IO 耗时
  3. LayoutInflater.createViewFromTag:通过 Factory2Factory 和 反射创建 View 【备注:mPrivateFactory 为创建 Fragment 用的】

从上述流程可知两个耗时点:加载 xml 的 IO 耗时,反射创建 View 耗时

【说明】Factory2AppCompatActivity.onCraete 中设置,用于将 TextView 转换成 AppCompatTextView 等适配

【注意】如果要手动设置 Factory2 要参考 AppCompatViewInflater 的兼容实现

获取界面布局耗时

参考:获取界面布局耗时

布局加载优化

一、异步创建布局:AsyncLayoutInflater

原理:内部开了单独一个线程专门用于异步创建 View,采用 ArrayBlockingQueue 队列做无限循环取数据,对于消息类还做了 SynchronizedPool 对象池,在子线程装载 View 失败还会在主线程再次装载一次

【注意一】由于采用系统自身的 Inflater 装载 View,会失去 AppCompat 的兼容效果,可以将源码拷贝出来修改成兼容将 TextView 转换成 AppCompatTextView 等适配

【注意二】由于是在子线程进行装载,待装载的 View 内部不能有依赖主线程的操作

二、Java 代码写布局

通过 xml 的方式编写布局,会有 IO 和反射耗时,可通过 Java 代码的方式从本质上解决性能问题。

该方式会引入新问题:不便于开发、可维护性差

三、X2C 方案

项目地址:https://github.com/iReaderAndroid/X2C

原理:通过 APT 编译期将 xml 文件翻译为 Java 布局文件

该方案保留了 xml 的优点,同时从根本上解决性能问题

【注意】该方案存在兼容性问题,部分属性 Java 代码时不支持的,同时也失去了 AppCompat 的兼容效果

进阶布局优化

一、异步布局框架 Litho

Litho 是 Facebook 开源的一款在Android上高效建立UI的声明式框架

PS:这里没有研究,自行参考官方文档实现方式

二、条件允许可以使用 Flutter

视图绘制优化

布局流程:测量 -> 布局 -> 绘制

  • 减少每个布局流程的耗时

  • 减少布局层级和复杂度:有的布局会多次测量,如果产生嵌套,就会呈几何上升的进行测量,推荐使用 ConstraintLayout 约束布局

  • 不嵌套使用 RelativeLayout,会对子 View 做两次测量

  • 不在嵌套 LinearLayout 中使用 weight,会对子 View 做两次测量

  • merge 标签:可以减少一个层级,用于根 View

  • ViewStub:可以在真正需要使用时才加载布局

  • 避免过度绘制:官方文档

    • 减少多余背景色,减少复杂的 Shape 的使用
    • 避层级叠加
    • 当在 onDraw 中绘制的内容前后有产生覆盖的,需要使用 clipRect 来屏蔽被覆盖的区域,如果需要绘制的对象不在剪裁范围内,需要直接返回,减少CPUGPU 的计算工作,如前后绘制了两个有重叠的Bitmap
  • 避免在 onDraw 中创建对象和执行耗时操作

  • Android 5.0 以下的自定义 View 触发刷新需要考虑刷新的范围 invalidate(int l, int t, int r, int b),5.0 以上的直接 invalidate() 即可

  • canvas 在使用离屏缓存时很耗资源,范围设置为所需范围即可

    1
    2
    3
    val count = canvas.saveLayer(100f, 100f, 200f, 200f, paint)
    // do something
    canvas.restoreToCount(count)
  • 通过 Systrace 发现每一帧都在 buildDrawingCache/SW (主线程) 或 buildLayer (渲染线程),那么需要检查代码的逻辑,尝试调整 LayerType 查看情况是否改良

    • 如果只是单纯的做动画,不动态修改 View 的内容,那么性能表现为 :Hardware Layer >= Software Layer > Normal Layer
    • 如果做动画同时动态修改 View 的内容,那么性能表现为 :Normal Layer > Software Layer = Hardware Layer