卡顿分析和UI渲染优化

一、卡顿问题本质分析

1.1、XML布局显示到屏幕

在分析卡顿问题本质原因之前,我们先简单了解一下XML布局加载以及到屏幕上显示。

Android的布局加载到展示,其实就是将对应的XML加载到内存,由框架解析成对应的控件对象,然后CPU根据控件对象携带的自己属性数据处理成多维图形。最终通过OPENGL调用GPU,GPU将多维图形进行栅格化处理渲染形成。

1.2、屏幕刷新的机制

Android Display 系统是通过VSync(垂直同步)信号来控制屏幕的刷新。每一个VSync过来时,就会从对应的buffer中取出CPU/GPU处理好的帧数据进行展示。所以整个流程必须要在一个VSync信号来临之前处理,按照60fps 1秒60帧的屏幕幕刷新计算,一个VSync信号大概就是16.66ms。所以整个流程就必须要在16.66ms内完成。如果这个时间点没有处理完就会导致漏帧、跳帧,严重的话就会造成画面的卡顿。

1.3、结论

所以从上述分析看,能造成卡顿的原因就是在规定的VSync信号的时间内CPU、GPU的渲染过程没有完成。而从1.1 XML加载到显示在屏幕这个流程上看,能造成CPU/GPU渲染超时的地方就一下三点:

  • XML布局加载解析成对应控件对象耗时。
  • CPU计算、处理数据耗时。
  • GPU渲染耗时。

二、如何分析卡顿问题

2.1、使用工具分析

2.1.1、 先通过Android手机开发者选项中的调试GPU过渡绘制,看一下过渡绘制区域

2.1.2、使用Layout Inspector查看布局层次。

2.1.3、使用systrace查看是否丢帧及丢帧时系统状态

  • systrace使用:

python systrace.py --t 5 --a 包名 -o mynewtrace.html sched freq idle am wm gfx view dalvik input res

注:systrace 只支持python2.x 同时需要下载python six包

  • systrace常用标签

gfx --> 图形系统,包括surfacefilnger, VSync等

input --> 分析滑动卡顿,按键、触摸的输入

view --> view绘制相关信息

am --> ActivityManager调用的相关信息

wm --> WindowManage相关信息

dalvik --> 虚拟机相关信息

sched --> CPU调度相关信息

disk --> IO相关

res --> 资源加载相关信息

  • systrace帧率球颜色

黄色 --> 漏帧=1帧

红色 --> 漏帧>1帧

绿色 --> 正常

  • 线程执行状态颜色

绿色 --> 有时间片正在跑

蓝色 --> 等待CPU分配时间片执行

紫色 --> 可中断休眠 如GC

橙色 --> 不可中断的休眠,如在等待IO

白色 --> 在休眠,如线程被互斥锁阻塞、IBinder阻塞、sleep、wait

2.1.4、利用Looper机制

Looper在转发消息是会涉及到日志的打印,而且是在执行前后的日志打印,所以我们可以通过 setMessageLogging方法设置一个Printer,而我们就可以在自定义的Printer里面记录开始时间和结束时间,通过这两个时间计算出是否有卡顿问题,并且可以直接输出引起卡顿的堆栈日志。

ini 复制代码
public static void loop() {
    final Looper me = myLooper();
    ....省略无数代码...
    
    final MessageQueue queue = me.mQueue;
    
   ....省略无数代码...

    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

        // This must be in a local variable, in case a UI event sets the logger
        final Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }

        final long traceTag = me.mTraceTag;
        long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
        long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
        if (thresholdOverride > 0) {
            slowDispatchThresholdMs = thresholdOverride;
            slowDeliveryThresholdMs = thresholdOverride;
        }
        final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
        final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);

        final boolean needStartTime = logSlowDelivery || logSlowDispatch;
        final boolean needEndTime = logSlowDispatch;

        if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
            Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
        }

        final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
        final long dispatchEnd;
        try {
            msg.target.dispatchMessage(msg);
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }

      ....省略无数代码...

        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }

        msg.recycleUnchecked();
    }
}

2.1.5、利用Choreographer监听帧率

Choreographer是协调屏幕刷新的,在这里会接收VSync信号,控制屏幕刷新。同时Choreographer提供了一个postFrameCallback方法,因此通过FrameCallbck的回调时间戳,也可以计算出是否有漏帧

三、优化思路

3.1、先利用手机GPU过渡绘制工具,查看是否有过渡绘制情况,过渡绘制情况又分为一下两种:

3.1.1、布局层次嵌套过深引起的过渡绘制

  • 解决方案:
  1. 优化布局层次,是布局扁平化,
  2. 使用include、merge、viewstub等标签优化。
  3. 使用ConstraintLayout约束布局

3.1.2、布局中存在溢出的背景色渲染

  • 解决方案:
  1. 找UI提供能解决方案的切图
  2. 裁剪出没必要的绘制元素

3.2、通过systrace查询漏帧时,系统的工作状态。

这种时候的漏帧一般有可能时CPU过载,被占用,或者线程被阻塞,binder阻塞等。这种情况下可以通过下一节具体分析

3.3、通过Looper机制、成熟三方框架如BlockCanary进行代码堆栈分析

相关推荐
带电的小王43 分钟前
WhisperKit: Android 端测试 Whisper -- Android手机(Qualcomm GPU)部署音频大模型
android·智能手机·whisper·qualcomm
梦想平凡1 小时前
PHP 微信棋牌开发全解析:高级教程
android·数据库·oracle
元争栈道1 小时前
webview和H5来实现的android短视频(短剧)音视频播放依赖控件
android·音视频
码农老起2 小时前
企业如何通过TDSQL实现高效数据库迁移与性能优化
数据库·性能优化
java_heartLake2 小时前
Vue3之性能优化
javascript·vue.js·性能优化
arnold662 小时前
探索 ElasticSearch:性能优化之道
大数据·elasticsearch·性能优化
阿甘知识库2 小时前
宝塔面板跨服务器数据同步教程:双机备份零停机
android·运维·服务器·备份·同步·宝塔面板·建站
元争栈道3 小时前
webview+H5来实现的android短视频(短剧)音视频播放依赖控件资源
android·音视频
MuYe3 小时前
Android Hook - 动态加载so库
android
OopspoO3 小时前
qcow2镜像大小压缩
学习·性能优化