美文网首页Android 性能优化篇
性能优化之 Skipped Frames

性能优化之 Skipped Frames

作者: Tsm_2020 | 来源:发表于2023-08-07 10:01 被阅读0次

    在开发过程中发现页面卡顿的时就能在Logcat 中发现如下的日志

    Skipped 30 frames! The application may be doing too much work on its main thread.
    

    这个日志就是在提醒我们需要做性能优化,关于这个掉帧的具体由来可以查看我的另一篇博客 Choreographer 编舞者的艺术(https://www.jianshu.com/p/3650dc59976a) 中查看,这篇文章我们主要来说一下针对这种情况如何排查,

    消息队列机制

    image.png

    上面画的这张图片阐述了一种丢帧的可能,就是在屏障消息添加之前,我们向MessageQueue中添加了一些比较耗时的操作,导致消息屏障执行时机太晚,
    在这个方向上我们就需要知道Looper 到底在处理哪一个消息,这个消息耗时了多久,其实这些东西在Looper的设计初衷时就给我们预留了后门,下面贴一下相关代码

    image.png
    
    public static void loop() {
          final Looper me = myLooper();
           // 省略代码....
          for (;;) {
              Message msg = queue.next(); // might block
              if (msg == null) {
                  // No message indicates that the message queue is quitting.
                  return;
              }
              final Printer logging = me.mLogging;
              if (logging != null) {
                  logging.println(">>>>> Dispatching to " + msg.target + " " +
                          msg.callback + ": " + msg.what);
              }
              ////省略代码...
              if (logging != null) {
                  logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
              }
          }
      }
    

    在looper 的loop 方法中他会轮序去处理消息,得到消息后,他会判断一下是否有Priner 来判断输出消息开始处理和结束处理的日志信息,我们可以通过这些东西来判断msg 处理是否合理

    Choreographer 队列机制

    
            doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
            doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
            doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos);
            doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
    

    刷新处理的时机也与 input 事件 动画等息息相关,如果在 Choreographer CallbackQueue 队列数组中的任意一个环节出现了执行时间过程,可能都会导致丢帧
    从网上看过一些大神的文章,他们的做法是使用 反射的方式,在 doframe时向 CallbackQueue 数组中添加添加一个头部消息,这样数组中第一个消息处理的时机就是当前链表的开始时间,下一个头部既是下一组事件的开始时间,同时也是上一组事件的结束时间,来判断 这 4 个环节哪一个出现了问题,
    第一个数据就代表的是 input 事件, 例如 event 等
    第二个代表的是动画事件 复杂的动画,或者同一时间执行的动画比较多,可能导致丢帧
    第三个代表的是插入动画事件
    第四个代表的是整个绘制流程 从 performMeasure 一直到 onDraw 方法的耗时 ,这个节点经常出现问题

    知道了在哪一个环节出现了问题,剩下的问题就比较好做了,
    我遇到过的 就是 在onDraw 中 频繁的处理和销毁对象导致频繁的gc ,gc 的过程中为了保证指针的准确性 jvm 会 Stop The Word, 让整个系统都停一下,等待他处理完在继续工作,这就会导致丢帧

    还遇到过布局层次不合理, 重复设置无用背景导致过度绘制 ,

    也使用过canvas.clipPaht 来约束绘制边界来预防过度绘制等问题,

    也遇到过在onCreate中确实需要初始化好多的控件,导致加载过慢,也可以使用 StubView 在请求结果后再inflate 控件,减少 在同一时间的工作量来达到优化的部分内容

    也遇到过在打开activity 在onCreate中初始化复杂的动画,导致第一帧帧既要做动画,又要做一些测量 布局 绘制等工作导致的,在同一帧执行的任务比较多而导致的少量丢帧

    应该还有其他的可以优化的点,但是我暂时就想到了这么多,如果你认为还有其他的,请联系我一起探讨,共同进步!

    相关文章

      网友评论

        本文标题:性能优化之 Skipped Frames

        本文链接:https://www.haomeiwen.com/subject/dytwpdtx.html