当前位置: 首页 > news >正文

Android性能:通过Choreographer检测UI丢帧和卡顿

                                         Android性能:通过Choreographer检测UI丢帧和卡顿

Android系统每隔16ms重绘UI界面,16ms是因为Android系统规定UI绘图的刷新频率60FPS。Android系统每隔16ms,发送一个系统级别信号VSYNC唤起重绘操作。1秒内绘制UI界面60次。每16ms为一个UI界面绘制周期。
平常所说的“丢帧”情况,并不是真的把绘图的帧给“丢失”了,也而是UI绘图的操作没有和系统16ms的绘图更新频率步调一致,开发者代码在绘图中绘制操作太多,导致操作的时间超过16ms,在Android系统需要在16ms时需要重绘的时刻由于UI线程被阻塞而绘制失败。如果丢的帧数量是一两帧,用户在视觉上没有明显感觉,但是如果超过3帧,用户就有视觉上的感知。丢帧数如果再持续增多,在视觉上就是所谓的“卡顿”。
丢帧是引起卡顿的重要原因。在Android中可以通过Choreographer检测Android系统的丢帧情况,以作为进一步分析卡顿的基础:

package zhangphil.test;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.Choreographer;
import android.view.View;

public class ANRActivity extends AppCompatActivity {

    private MyFrameCallback mFrameCallback = new MyFrameCallback();

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Choreographer.getInstance().postFrameCallback(mFrameCallback);

        setContentView(R.layout.activity_anr);
        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                uiLongTimeWork();
            }
        });
    }

    public class MyFrameCallback implements Choreographer.FrameCallback {
        private String TAG = "性能检测";
        private long lastTime = 0;

        @Override
        public void doFrame(long frameTimeNanos) {
            if (lastTime == 0) {
                //代码第一次初始化。不做检测统计。
                lastTime = frameTimeNanos;
            } else {
                long times = (frameTimeNanos - lastTime) / 1000000;
                int frames = (int) (times / 16);

                if (times > 16) {
                    Log.w(TAG, "UI线程超时(超过16ms):" + times + "ms" + " , 丢帧:" + frames);
                }

                lastTime = frameTimeNanos;
            }

            Choreographer.getInstance().postFrameCallback(mFrameCallback);
        }
    }

    private void uiLongTimeWork() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

 

Choreographer周期性的在UI重绘时候触发,在代码中记录上一次和下一次绘制的时间间隔,如果超过16ms,就意味着一次UI线程重绘的“丢帧”。丢帧的数量为间隔时间除以16,如果超过3,就开始有卡顿的感知。

代码运行输出:

07-20 11:23:40.082 5654-5654/zhangphil.test W/性能检测: UI线程超时(超过16ms):17ms , 丢帧:1
07-20 11:23:40.099 5654-5654/zhangphil.test W/性能检测: UI线程超时(超过16ms):17ms , 丢帧:1
07-20 11:23:40.145 5654-5654/zhangphil.test W/性能检测: UI线程超时(超过16ms):28ms , 丢帧:1
07-20 11:23:40.165 5654-5654/zhangphil.test W/性能检测: UI线程超时(超过16ms):20ms , 丢帧:1
07-20 11:23:40.190 5654-5654/zhangphil.test W/性能检测: UI线程超时(超过16ms):24ms , 丢帧:1
07-20 11:23:40.208 5654-5654/zhangphil.test W/性能检测: UI线程超时(超过16ms):17ms , 丢帧:1
07-20 11:23:40.224 5654-5654/zhangphil.test W/性能检测: UI线程超时(超过16ms):17ms , 丢帧:1
07-20 11:23:40.257 5654-5654/zhangphil.test W/性能检测: UI线程超时(超过16ms):33ms , 丢帧:2
07-20 11:23:40.306 5654-5654/zhangphil.test W/性能检测: UI线程超时(超过16ms):24ms , 丢帧:1

 

如果手动点击按钮故意阻塞1秒,丢弃的帧数更多。

相关文章:

  • java提高篇(五)-----使用序列化实现对象的拷贝
  • java小心机(3)| 浅析finalize()
  • Adapter Class Cast Exception Removing Footer View from ListView
  • LeetCode--014--最长公共前缀
  • 串口超时处理原理及实现
  • ACM北大暑期课培训第一天
  • Delphi窗体创建释放过程及单元文件小结(转)
  • Linux部署zabbix3.4 结合钉钉智能报警
  • 学生分数排序
  • zabbix3.4上简单web监测功能测试
  • linux系统man查询命令等级与意义
  • 关于ES6的Promise的使用深入理解
  • P1065 津津的储蓄计划
  • 2018年 7月总结8月计划
  • Proteus仿真_01、 8086 IO译码仿真
  • 【347天】每日项目总结系列085(2018.01.18)
  • 【Under-the-hood-ReactJS-Part0】React源码解读
  • 0x05 Python数据分析,Anaconda八斩刀
  • css布局,左右固定中间自适应实现
  • DOM的那些事
  • Electron入门介绍
  • Java多线程(4):使用线程池执行定时任务
  • Octave 入门
  • PHP 使用 Swoole - TaskWorker 实现异步操作 Mysql
  • React 快速上手 - 06 容器组件、展示组件、操作组件
  • Vue学习第二天
  • Zepto.js源码学习之二
  • 等保2.0 | 几维安全发布等保检测、等保加固专版 加速企业等保合规
  • 关于使用markdown的方法(引自CSDN教程)
  • 力扣(LeetCode)56
  • 一、python与pycharm的安装
  • 中文输入法与React文本输入框的问题与解决方案
  • SAP CRM里Lead通过工作流自动创建Opportunity的原理讲解 ...
  • ​ssh免密码登录设置及问题总结
  • ​第20课 在Android Native开发中加入新的C++类
  • #HarmonyOS:基础语法
  • #pragma 指令
  • #stm32驱动外设模块总结w5500模块
  • %3cscript放入php,跟bWAPP学WEB安全(PHP代码)--XSS跨站脚本攻击
  • (2021|NIPS,扩散,无条件分数估计,条件分数估计)无分类器引导扩散
  • (rabbitmq的高级特性)消息可靠性
  • (读书笔记)Javascript高级程序设计---ECMAScript基础
  • (附源码)apringboot计算机专业大学生就业指南 毕业设计061355
  • (生成器)yield与(迭代器)generator
  • (十六)Flask之蓝图
  • (一)使用Mybatis实现在student数据库中插入一个学生信息
  • (已更新)关于Visual Studio 2019安装时VS installer无法下载文件,进度条为0,显示网络有问题的解决办法
  • .NET 5种线程安全集合
  • .NET Core 将实体类转换为 SQL(ORM 映射)
  • .NET MVC第三章、三种传值方式
  • .NET 反射的使用
  • .Net 中Partitioner static与dynamic的性能对比
  • .net图片验证码生成、点击刷新及验证输入是否正确
  • /etc/apt/sources.list 和 /etc/apt/sources.list.d
  • @Autowired注解的实现原理