字节跳动青训营bitdance tg-----布局优化-

105 阅读4分钟

[这是我参与「第四届青训营 」笔记创作活动的第2天]

帧率

帧率(frame per second,即 FPS),指的是每秒刷新的次数。一般电影的帧率为 24FPS、25FPS 和 30FPS。而游戏的帧率一般要保持 60FPS 才能叫做流畅,当游戏的 FPS 低于 30 时,我们就会感受到明显地卡顿。

Android 系统每隔 1s/60FPS = 16ms 触发一次 UI 刷新操作,这就要求我们的应用都能在 16ms 内绘制完成。如果有一次的界面绘制用了 22ms,那么,用户在 32ms 内看见的都是同一个界面,也就是丢帧(dropped frame) 了。情况严重的就会让用户感受到应用运行”卡顿“。

卡顿原因

  • 过于复杂的布局

    UI 渲染的整个过程可以分为具体绘制之前的运算(包括 onMeasure、onLayout、onDraw)和真正的绘制到屏幕两部分,如果布局层级太深或者自定义控件中的运算太复杂,所需相关的运算时间超过 16ms,就会造成卡顿现象。

  • 过度绘制(Overdraw)

    如果在绘制过程中存在 Overdraw 也可能导致卡顿

    Overdraw 是用来描述屏幕上的像素在一帧内被重绘次数的一个概念。

    理想情况下, 每屏每帧上, 每个像素点应该只被绘制一次, 如果有多次绘制, 就是Overdraw, 过度绘制了

  • 频繁 GC

    GC 会停顿所有的线程,造成卡顿。

    导致频繁GC有两个原因

    • 内存抖动(Memory Churn) ,即大量的对象被创建又在短时间内马上被释放
    • 瞬间产生大量的对象会严重占用 Young Generation 的内存区域,当达到阀值,剩余空间不够的时候,也会触发GC。即使每次分配的对象需要占用很少的内存,但是他们叠加在一起会增加Heap的压力,从而触发更多的GC。

    一般来说瞬间大量产生对象一般是因为我们在代码的循环中 new 对象, 或是在 onDraw 中创建对象等

  • UI线程的复杂运算

    UI线程的复杂运算会造成 UI 响应停滞,卡顿,甚至是 ANR。

优化方案

针对绘制前

  • 减少 View 层级,加快 View 的循环遍历过程
  • 减少在子 View 中使用 margin、padding,在父 View 中设置 margin、padding,避免在子 View 中单独运算
  • 使用布局标签

针对绘制时

  • 去除不必要的背景,加快 View 的绘制
  • 去除不必要的装饰器,慎用渐变,减少绘制流程

布局标签

< include />

Android 为我们提供了 < include /> 标签,通过它,我们可以将这些共用的组件抽取出来单独放到一个 xml 文件中,然后使用 < include /> 标签导入共用布局。

< merge />

< merge /> 标签的作用是合并 UI 布局,使用该标签能降低 UI 布局的嵌套层次。

该标签的主要使用场景主要包括两个

第一是当 xml 文件的根布局是 FrameLayout 时,可以用 merge 作为根节点。理由是因为 Activity 的内容布局中,默认就用了一个 FrameLayout 作为 xml 布局根节点的父节点。

第二是当用 < include /> 标签导入一个共用布局时,如果父布局和子布局根节点为同一类型,可以使用 < merge /> 将子节点布局的内容合并包含到父布局中,这样就可以减少一级嵌套层次。

< ViewStub />

ViewStub 是一个不可见的,能在运行期间延迟加载的大小为 0 的 View,它直接继承于View。

当对一个 ViewStub 调用 inflate() 方法或设置它可见时,系统会加载在 ViewStub 标签中引入的我们自己定义的 View,然后填充在父布局当中,在这之前,它是不占用布局空间和系统资源的。

它的使用场景可以是在我们需要加载并显示一些不常用的 View 时,例如一些网络异常的提示信息等。

工具

\