云米客户端APM性能监控组件分析(三)

2020-04-09  本文已影响0人  捉影T_T900

上一篇文章已经分析了卡顿监控原理,现在来分析监控绘制帧率的原理和技巧。

先了解下手机APP的画面是怎么显示出来的。计算机画图其实很蠢,只能一次画一张图像,但它可以画得很快,如果图像是连贯的画面,那人眼看着高速切换的画面感觉就像是整个界面动起来了。小时候手工课做过走马灯的话,都知道这是什么原理。那要多快才能令人感受到画面是流程的呢?要达到每秒画60张图像,就是每秒60帧,人手肯定达不到这个速度,但计算机可以做到。

问题一:有什么方法可以获得手机的绘制帧率?

这里不打算深入讲解Android手机的绘图过程,也不打算讲解Android系统的图形绘制信号机制。Google在Android 4.1后增加了Choreographer机制,用于用于同Vsync机制配合,统一动画、输入和绘制时机。Choreographer内部公开了FrameCallback这个接口,Android系统每画一帧画面就会回调FrameCallback的doFrame(long frameTimeNanos)方法。这就是一个很好的切入口,Android 4.1之前的手机怎么办?你现在找一台Android 4.1之前的手机给我看看?能找到算我输【摊手】

问题二:现在核心问题来了,怎么算这个帧率?

doFrame(long frameTimeNanos)回调拿到的只是一个时间戳,就是画当前帧的时刻。帧率是怎么算的呢?帧率是每秒画了多少幅图像。那怎么算才好?
维护一个计数器,用于记录1秒内doFrame了多少次。维护一个runnable,每隔1秒计算一次帧率(mFpsCount / costTotalTime)。

公式:帧数 ÷ (记录开始时间 - 记录结束时间)= 帧率

private long mLastFrameTimeNanos = 0; // 最后一次时间
    private long mFrameTimeNanos = 0; // 本次的当前时间
    private int mFpsCount = 0;

    private Runnable runnable = new Runnable() {
        @Override
        public void run() {
            if (!isCanWork()) {
                return;
            }

            calculateFPS();

            AsyncThreadTask.executeDelayed(runnable, TaskConfig.FPS_INTERVAL);
        }
     };

    @Override
    public void doFrame(long frameTimeNanos) {
        mFpsCount++;
        mFrameTimeNanos = frameTimeNanos;
        if (isCanWork()) {
            Choreographer.getInstance().postFrameCallback(this);
        }
    }

   private void calculateFPS() {
        if (mLastFrameTimeNanos == 0) {
            mLastFrameTimeNanos = mFrameTimeNanos;
            return;
        }

        float costTime = (float)(mFrameTimeNanos - mLastFrameTimeNanos) / 1000000.0F; // 转换成单位秒
        if (mFpsCount <= 0 && costTime <= 0.0F) {
            return;
        }

        // 每一帧花费的时间就是帧率,每秒画了多少帧就是帧率
        int fpsResult = (int)(mFpsCount * 1000 / costTime);
        if (fpsResult < 0) {
            return;
        }

        if (fpsResult <= 30) { // 如果刷新帧率小于30,则视为掉帧,保存帧率信息
           //  满足掉帧的条件,做你想做的事情
        }

        mLastFrameTimeNanos = mFrameTimeNanos;
        mFpsCount = 0;
    }

为什么要小于30才处理?因为满足60帧率的话很容易就触发了,条件太苛刻。

未完待续......
上一篇 下一篇

猜你喜欢

热点阅读