Fps统计方案

SM(Smooth,流畅度)、SF(SkippedFrame,跳帧)

基础数据来源背景: 从Android 4.1(版本代号为Jelly Bean)开始,Android OS开发团队便引入了三个核心元素,即VSYNC、Triple Buffer和Choreographer力图解决严重影响Android口碑问题之一的UI流畅性差的问题。 而其中,Choreographer的主要功能是,当收到VSYNC信号时,在doFrame()里去调用使用者通过postCallback设置的回调函数。目前一共定义了三种类型的回调,它们分别是: CALLBACK_INPUT:优先级最高,和输入事件处理有关。 CALLBACK_ANIMATION:优先级其次,和Animation的处理有关。 CALLBACK_TRAVERSAL:优先级最低,和UI等控件绘制有关。这个函数里面主要是检查当前窗口当前状态,比如说是否依然可见,尺寸,方向,布局是否发生改变(可能是由前面的用户输入触发的),分别调用performMeasure(), performLayout(), performDraw()来完成测量,布局和绘制工作。 由于VSYNC信号每16.6ms产生一次(具体时间依据系统实现),因此理想情况下每秒可执行绘制相关操作60次。但实际情况是,CALLBACK_INPUT、CALLBACK_ANIMATION、CALLBACK_TRAVERSAL中任意一个节点都有可能因为存在耗时操作而导致doFrame()无法在16.6ms内完成,从而无法在接下来的VSYNC信号到达时进行处理。 因此,SM(Smooth,流畅度)、SF(SkippedFrame,跳帧均选用Choreographer中doFrame()实际执行的次数作为基础数据。

指标计算方法: SF(SkippedFrame,跳帧),即为某个应用程序在单位时间1秒内,跳过执行Choreographer中doFrame()的次数。 SM(Smooth,流畅度),即为某个应用程序在单位时间1秒内,实际执行Choreographer中doFrame()的次数。在获取SF数值的基础上,SM=60-SF。 连续跳帧,与SF的不同在于,它将跳帧按其连续跳帧数进行统计,用以衡量单位时间内的卡顿程度。

import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.PixelFormat;
import android.os.Build;
import android.os.SystemClock;
import android.view.Choreographer;
import android.view.Gravity;
import android.view.View;
import android.view.WindowManager;

public class MonitorFPS {
    private static final long FAKE_FRAME_TIME = 10;
    private static final Long MONITOR_INTERVAL = 200L;//设置获取fps的时间为200ms
    private static final Long MONITOR_INTERVAL_NANOS = MONITOR_INTERVAL * 1000L * 1000L;
    private static final Long MAX_INTERVAL = 1000L;//设置计算fps的单位时间间隔1000ms,即fps/s;
    private FPSRecordView mFPSFpsRecordView = null;
    private WindowManager mWindowManager = null;
    private volatile boolean mFPSState = false;
    private IFPSCallBack mIFPSCallBack;
    private String mType;
    private Choreographer.FrameCallback mFrameCallback;

    /**
     * @param context
     * @param type
     */
    public MonitorFPS (Context context, String type) {
        mType = type;
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
            mWindowManager = (WindowManager) context.getSystemService(Activity.WINDOW_SERVICE);
            mFPSFpsRecordView = new FPSRecordView(context);
        }
    }

    public interface IFPSCallBack {
        void fpsCallBack(double fps);
    }

    public boolean getMonitorFPSStatus() {
        return mFPSState;
    }
    public void setIFPSCallBack(IFPSCallBack ifpsCallBack) {
        mIFPSCallBack = ifpsCallBack;
    }
    public synchronized void stop() {
        if (mFPSState) {
            mFPSState = false;
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
                try {
                    mWindowManager.removeView(mFPSFpsRecordView);
                    mFPSFpsRecordView.mStartTime = -1;
                    mFPSFpsRecordView.mCounter = 0;
                } catch (Exception e) {

                }
            } else if (mFrameCallback != null){
                Choreographer.getInstance().removeFrameCallback(mFrameCallback);
            }
        }
    }
    public void start() {
        if (!mFPSState) {
            mFPSState = true;
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
                startBase();
            } else {
                startJellyBean();
            }
        }
    }

    private void startBase() {
        mFPSFpsRecordView.mStartTime = -1;
        // UI线程插入空view 计算fps
        WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.WRAP_CONTENT,
//                    WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, 0,
                WindowManager.LayoutParams.TYPE_TOAST, 0,
                PixelFormat.TRANSLUCENT);
        params.gravity = Gravity.LEFT | Gravity.TOP;
        params.flags =WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
        params.height = 1;
        params.width = 1;
        try {
            mWindowManager.removeView(mFPSFpsRecordView);
        } catch (Exception e) {

        }
        mWindowManager.addView(mFPSFpsRecordView, params);
        mFPSFpsRecordView.postDelayed(new Runnable() {
            public void run() {
                // 停止Fps计算
                if (mFPSState) {
                    mFPSFpsRecordView.invalidate();
                    mFPSFpsRecordView.postDelayed(this, FAKE_FRAME_TIME);
                }
            }
        }, FAKE_FRAME_TIME);
    }

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    private void startJellyBean() {
        mFrameCallback = new Choreographer.FrameCallback() {
            private long mStartTime = -1;
            private int mCounter = 0;

            @Override
            public void doFrame(long frameTimeNanos) {
                if (mStartTime == -1) {
                    mStartTime = frameTimeNanos;
                }
                long interval = frameTimeNanos - mStartTime;
                if (interval > MONITOR_INTERVAL_NANOS) {
                    double fps = (((double)(mCounter * 1000L * 1000L)) / interval) * MAX_INTERVAL;
                    if (mIFPSCallBack != null) {
                        mIFPSCallBack.fpsCallBack(fps);
                    }
                    MonitorUtils.monitorOnTimer(mType, "fps", (float)fps);
                    mFPSState = false;
                } else {
                    ++mCounter;
                    Choreographer.getInstance().postFrameCallback(this);
                }
            }
        };
        Choreographer.getInstance().postFrameCallback(mFrameCallback);
    }

    private class FPSRecordView extends View {
        private long mStartTime = -1;
        private int mCounter = 0;
        public FPSRecordView(Context context) {
            super(context);
        }

        @Override
        protected void onDraw(Canvas canvas) {
            if (mStartTime == -1) {
                mStartTime = SystemClock.elapsedRealtime();
                mCounter = 0;
            }
            long realInterval = SystemClock.elapsedRealtime()-mStartTime;
            if (realInterval > MONITOR_INTERVAL) {
                double fps = ((double) mCounter/realInterval)*MAX_INTERVAL;
                if (mIFPSCallBack != null) {
                    mIFPSCallBack.fpsCallBack(fps);
                }
                MonitorUtils.monitorOnTimer(mType,"fps",(float)fps);
                MonitorFPS.this.stop();
            }
            mCounter++;
        }

    }
}

复制代码

转载于:https://juejin.im/post/5a420c1e6fb9a045167d730c

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值