深入解析 Matrix FrameTracer:Android 帧率监控的实现与源码剖析

在移动应用性能优化领域,帧率(FPS)是衡量 UI 流畅度的核心指标,而腾讯 Matrix 框架的FrameTracer模块,通过跨版本兼容设计与细粒度监控能力,成为 Android 端帧率监控的标杆实现。本文基于 Matrix 开源源码(含FrameTracerUIThreadMonitorLooperMonitor等核心类),从技术架构、关键实现到源码解析,揭示其底层逻辑。

一、FrameTracer 的核心定位与分层架构

  1. 基础监控层 :低版本(API<24)依赖UIThreadMonitor+Choreographer捕获帧周期,高版本(API≥24)叠加FrameMetrics获取渲染阶段耗时;
  2. 数据收集层 :通过SceneFrameCollector按界面(Activity)隔离帧率数据,避免跨场景干扰;
  3. 卡顿溯源层LooperMonitor监听主线程消息耗时,定位阻塞渲染的具体任务;
  4. 上报层AllSceneFrameListener按周期聚合数据并上报,支撑性能问题分析。

这种分层设计既覆盖了 99% 以上 Android 设备,又实现了 "从帧率异常到根因定位" 的闭环。

二、关键实现与源码解析

1. 低版本兼容:UIThreadMonitor+Choreographer 的帧跟踪(API<24)

Android 7.0(API 24)前无FrameMetricsFrameTracer通过UIThreadMonitor反射 HookChoreographer的核心回调队列,实现帧周期监控。

1.1 UIThreadMonitor 初始化:反射获取 Choreographer 资源

UIThreadMonitor是低版本监控的核心,通过反射突破Choreographer的隐藏 API 限制,获取帧回调队列:

csharp 复制代码
// 源码位置:com.tencent.matrix.trace.core.UIThreadMonitor.java
public void init(TraceConfig config) {
    this.config = config;
    // 必须在主线程初始化(UI渲染逻辑绑定主线程)
    if (Thread.currentThread() != Looper.getMainLooper().getThread()) {
        throw new AssertionError("must be init in main thread!");
    }

    // 1. 注册Looper监控(后续卡顿溯源用)
    LooperMonitor.register(new LooperMonitor.LooperDispatchListener(
        config.historyMsgRecorder, config.denseMsgTracer) {
        @Override
        public boolean isValid() { return isAlive; }
        @Override
        public void dispatchStart() { UIThreadMonitor.this.dispatchBegin(); }
        @Override
        public void dispatchEnd() { UIThreadMonitor.this.dispatchEnd(); }
    });

    this.isInit = true;
    // 2. 获取Choreographer实例(系统帧协调器)
    choreographer = Choreographer.getInstance();
    // 3. 反射获取Choreographer隐藏字段:帧间隔、锁、回调队列
    frameIntervalNanos = ReflectUtils.reflectObject(
        choreographer, "mFrameIntervalNanos", Constants.DEFAULT_FRAME_DURATION); // 默认16.6ms(60Hz)
    callbackQueueLock = ReflectUtils.reflectObject(choreographer, "mLock", new Object());
    callbackQueues = ReflectUtils.reflectObject(choreographer, "mCallbackQueues", null);

    // 4. 反射获取三个核心回调队列的add方法(输入、动画、绘制)
    if (null != callbackQueues) {
        addInputQueue = ReflectUtils.reflectMethod(
            callbackQueues[CALLBACK_INPUT], "addCallbackLocked", long.class, Object.class, Object.class);
        addAnimationQueue = ReflectUtils.reflectMethod(
            callbackQueues[CALLBACK_ANIMATION], "addCallbackLocked", long.class, Object.class, Object.class);
        addTraversalQueue = ReflectUtils.reflectMethod(
            callbackQueues[CALLBACK_TRAVERSAL], "addCallbackLocked", long.class, Object.class, Object.class);
    }
    MatrixLog.i(TAG, "[init] frameIntervalNanos:%s", frameIntervalNanos);
}

核心逻辑

  • 通过ReflectUtils反射获取ChoreographermFrameIntervalNanos(帧间隔)、mCallbackQueues(回调队列数组);
  • 注册LooperDispatchListener,关联主线程消息调度与帧监控,用于后续卡顿溯源。

1.2 帧回调注入:监听 Choreographer 三阶段耗时

UIThreadMonitor通过addFrameCallbackChoreographer的三个核心队列(输入、动画、绘制)注入自定义回调,拆分每帧各阶段耗时:

typescript 复制代码
// 源码位置:com.tencent.matrix.trace.core.UIThreadMonitor.java
private synchronized void addFrameCallback(int type, Runnable callback, boolean isAddHeader) {
    if (callbackExist[type]) {
        MatrixLog.w(TAG, "[addFrameCallback] this type %s callback has exist!", type);
        return;
    }
    try {
        synchronized (callbackQueueLock) { // Choreographer的回调队列锁
            Method method = null;
            // 按类型选择对应的队列add方法
            switch (type) {
                case CALLBACK_INPUT: method = addInputQueue; break;
                case CALLBACK_ANIMATION: method = addAnimationQueue; break;
                case CALLBACK_TRAVERSAL: method = addTraversalQueue; break;
            }
            if (null != method) {
                // 注入回调:isAddHeader=true表示插入队列头部,确保优先执行
                method.invoke(callbackQueues[type], 
                    !isAddHeader ? SystemClock.uptimeMillis() : -1, callback, null);
                callbackExist[type] = true;
            }
        }
    } catch (Exception e) {
        MatrixLog.e(TAG, e.toString());
    }
}

// 帧回调核心逻辑(UIThreadMonitor实现Runnable)
@Override
public void run() {
    final long start = System.nanoTime();
    try {
        doFrameBegin(token); // 标记帧开始
        doQueueBegin(CALLBACK_INPUT); // 记录输入阶段开始时间
        
        // 1. 注入动画阶段回调:输入阶段结束后触发
        addFrameCallback(CALLBACK_ANIMATION, new Runnable() {
            @Override
            public void run() {
                doQueueEnd(CALLBACK_INPUT); // 计算输入阶段耗时
                doQueueBegin(CALLBACK_ANIMATION); // 标记动画阶段开始
            }
        }, true);

        // 2. 注入绘制阶段回调:动画阶段结束后触发
        addFrameCallback(CALLBACK_TRAVERSAL, new Runnable() {
            @Override
            public void run() {
                doQueueEnd(CALLBACK_ANIMATION); // 计算动画阶段耗时
                doQueueBegin(CALLBACK_TRAVERSAL); // 标记绘制阶段开始
            }
        }, true);
    } finally {
        if (config.isDevEnv()) {
            MatrixLog.d(TAG, "[run] inner cost:%sns", System.nanoTime() - start);
        }
    }
}

核心逻辑

  • CALLBACK_INPUT(输入)、CALLBACK_ANIMATION(动画)、CALLBACK_TRAVERSAL(绘制)三个队列注入回调,通过doQueueBegin/doQueueEnd记录各阶段时间戳;
  • 最终通过queueCost[type] = 结束时间 - 开始时间,得到每帧各阶段的耗时(如输入处理耗时、布局绘制耗时)。

1.3 低版本帧率计算

UIThreadMonitordispatchEnd中计算帧率与掉帧次数,核心逻辑如下:

scss 复制代码
// 源码位置:com.tencent.matrix.trace.core.UIThreadMonitor.java
private void dispatchEnd() {
    if (config.isFPSEnable()) {
        long startNs = token; // 帧开始时间戳
        long intendedFrameTimeNs = startNs;
        if (isVsyncFrame) {
            doFrameEnd(token); // 标记帧结束,计算各阶段总耗时
            intendedFrameTimeNs = getIntendedFrameTimeNs(startNs); // 获取理想帧时间
        }
        long endNs = System.nanoTime(); // 帧实际结束时间
        // 计算掉帧数:(实际总耗时 - 理想帧间隔) / 理想帧间隔
        long inputCost = queueCost[CALLBACK_INPUT];
        long animationCost = queueCost[CALLBACK_ANIMATION];
        long traversalCost = queueCost[CALLBACK_TRAVERSAL];

        // 通知FrameTracer处理数据
        synchronized (observers) {
            for (LooperObserver observer : observers) {
                if (observer.isDispatchBegin()) {
                    observer.doFrame(
                        AppActiveMatrixDelegate.INSTANCE.getVisibleScene(), // 当前界面
                        startNs, endNs, isVsyncFrame, intendedFrameTimeNs,
                        inputCost, animationCost, traversalCost
                    );
                }
            }
        }
    }
    // 省略其他逻辑...
}

2. 高版本增强:FrameMetrics+Activity 生命周期(API≥24)

Android 7.0 引入FrameMetrics,可直接获取 12 个渲染阶段耗时(如LAYOUT_MEASURE_DURATIONGPU_DURATION),FrameTracer通过它实现更细粒度的监控。

2.1 FrameMetrics 监听注册

FrameTracerActivity resumed 时动态注册FrameMetrics监听,确保仅监控前台界面:

java 复制代码
// 源码位置:com.tencent.matrix.trace.tracer.FrameTracer.java
@RequiresApi(Build.VERSION_CODES.N)
@Override
public void onActivityResumed(Activity activity) {
    if (frameListenerMap.containsKey(activity.hashCode())) {
        return;
    }
    // 1. 获取设备刷新率(适配120Hz/90Hz屏幕)
    defaultRefreshRate = getRefreshRate(activity.getWindow());
    MatrixLog.i(TAG, "default refresh rate is %dHz", (int) defaultRefreshRate);

    // 2. 创建FrameMetrics监听
    Window.OnFrameMetricsAvailableListener frameListener = new Window.OnFrameMetricsAvailableListener() {
        private float cachedRefreshRate = defaultRefreshRate;
        private float cachedThreshold = dropFrameListenerThreshold / 60f * cachedRefreshRate; // 动态掉帧阈值

        @Override
        public void onFrameMetricsAvailable(Window window, FrameMetrics frameMetrics, int dropCountSinceLastInvocation) {
            if (!isForeground()) return; // 过滤后台状态

            // 3. 过滤异常数据(部分设备会产生无效值)
            for (int i = FrameDuration.UNKNOWN_DELAY_DURATION.ordinal(); 
                 i <= FrameDuration.TOTAL_DURATION.ordinal(); i++) {
                long v = frameMetrics.getMetric(FrameDuration.indices[i]);
                if (v < 0 || v >= HALF_MAX) return;
            }

            // 4. 深拷贝FrameMetrics(避免系统回收数据)
            FrameMetrics frameMetricsCopy = new FrameMetrics(frameMetrics);
            // 5. 计算掉帧数:(实际总耗时 - 理想帧间隔) / 理想帧间隔
            long totalDuration = frameMetricsCopy.getMetric(FrameMetrics.TOTAL_DURATION);
            float frameIntervalNanos = Constants.TIME_SECOND_TO_NANO / cachedRefreshRate;
            float droppedFrames = Math.max(0f, (totalDuration - frameIntervalNanos) / frameIntervalNanos);
            droppedSum += droppedFrames;

            // 6. 掉帧阈值判断(超过阈值则通知监听器)
            if (dropFrameListener != null && droppedFrames >= cachedThreshold) {
                dropFrameListener.onFrameMetricsAvailable(
                    ProcessUILifecycleOwner.INSTANCE.getVisibleScene(),
                    frameMetricsCopy, droppedFrames, cachedRefreshRate
                );
            }

            // 7. 分发数据到收集器(如SceneFrameCollector)
            synchronized (listeners) {
                for (IFrameListener observer : listeners) {
                    observer.onFrameMetricsAvailable(
                        ProcessUILifecycleOwner.INSTANCE.getVisibleScene(),
                        frameMetricsCopy, droppedFrames, cachedRefreshRate
                    );
                }
            }
        }
    };

    // 8. 注册监听并缓存(避免重复注册)
    this.frameListenerMap.put(activity.hashCode(), frameListener);
    activity.getWindow().addOnFrameMetricsAvailableListener(
        frameListener, MatrixHandlerThread.getDefaultHandler()
    );
}

核心逻辑

  • 动态适配设备刷新率(如 120Hz 屏幕对应 8.3ms 理想帧间隔),避免固定阈值导致的误判;
  • 过滤异常数据(如负数或超大值),提升监控准确性;
  • 通过FrameMetrics.TOTAL_DURATION计算总渲染耗时,结合理想帧间隔得到掉帧数。

2.2 渲染阶段耗时解析

FrameTracer定义FrameDuration枚举,映射FrameMetrics的 10 个核心阶段,便于定位掉帧根源:

java 复制代码
// 源码位置:com.tencent.matrix.trace.tracer.FrameTracer.java
public enum FrameDuration {
    UNKNOWN_DELAY_DURATION, INPUT_HANDLING_DURATION, ANIMATION_DURATION, 
    LAYOUT_MEASURE_DURATION, DRAW_DURATION, SYNC_DURATION, 
    COMMAND_ISSUE_DURATION, SWAP_BUFFERS_DURATION, TOTAL_DURATION, GPU_DURATION;

    // 映射FrameMetrics的原生指标索引
    @SuppressLint("InlinedApi")
    static final int[] indices = {
        FrameMetrics.UNKNOWN_DELAY_DURATION, FrameMetrics.INPUT_HANDLING_DURATION,
        FrameMetrics.ANIMATION_DURATION, FrameMetrics.LAYOUT_MEASURE_DURATION,
        FrameMetrics.DRAW_DURATION, FrameMetrics.SYNC_DURATION,
        FrameMetrics.COMMAND_ISSUE_DURATION, FrameMetrics.SWAP_BUFFERS_DURATION,
        FrameMetrics.TOTAL_DURATION, FrameMetrics.GPU_DURATION
    };

    // 格式化输出各阶段耗时
    public static String stringify(long[] durations) {
        StringBuilder sb = new StringBuilder();
        sb.append('{');
        for (FrameDuration item : values()) {
            sb.append(item.name()).append('=').append(durations[item.ordinal()]).append("; ");
        }
        sb.setLength(sb.length() - 2);
        sb.append("}");
        return sb.toString();
    }
}

作用 :通过LAYOUT_MEASURE_DURATION可快速定位 "布局层级过深" 问题,通过GPU_DURATION可识别 "GPU 渲染阻塞"。

3. 场景化数据处理:SceneFrameCollector

FrameTracer通过SceneFrameCollector按界面(Activity)隔离帧率数据,避免跨场景数据混淆,核心是 "按场景聚合、定期上报"。

3.1 数据聚合逻辑

java 复制代码
// 源码位置:com.tencent.matrix.trace.tracer.FrameTracer.java
@RequiresApi(Build.VERSION_CODES.N)
private class SceneFrameCollector implements IFrameListener {
    private final Handler frameHandler = new Handler(MatrixHandlerThread.getDefaultHandlerThread().getLooper());
    // 按场景名缓存数据(指定场景+未指定场景)
    private final HashMap<String, SceneFrameCollectItem> specifiedSceneMap = new HashMap<>();
    private final HashMap<ISceneFrameListener, SceneFrameCollectItem> unspecifiedSceneMap = new HashMap<>();

    // 注册场景监听器(如AllSceneFrameListener)
    public synchronized void register(@NonNull ISceneFrameListener listener) {
        if (listener.getIntervalMs() < 1 || listener.getThreshold() < 0) {
            MatrixLog.e(TAG, "Illegal value, intervalMs=%d, threshold=%d",
                listener.getIntervalMs(), listener.getThreshold());
            return;
        }
        String scene = listener.getName();
        SceneFrameCollectItem collectItem = new SceneFrameCollectItem(listener);
        if (scene == null || scene.isEmpty()) {
            unspecifiedSceneMap.put(listener, collectItem);
        } else {
            specifiedSceneMap.put(scene, collectItem);
        }
    }

    // 接收FrameMetrics数据并聚合
    @Override
    public void onFrameMetricsAvailable(final String sceneName, final FrameMetrics frameMetrics, 
                                        final float droppedFrames, final float refreshRate) {
        frameHandler.post(new Runnable() { // 异步处理,避免阻塞主线程
            @Override
            public void run() {
                synchronized (SceneFrameCollector.this) {
                    // 1. 匹配指定场景的数据项
                    SceneFrameCollectItem collectItem = specifiedSceneMap.get(sceneName);
                    if (collectItem != null) {
                        collectItem.append(sceneName, frameMetrics, droppedFrames, refreshRate);
                    }
                    // 2. 分发到未指定场景的监听器(如全局监控)
                    for (SceneFrameCollectItem item : unspecifiedSceneMap.values()) {
                        item.append(sceneName, frameMetrics, droppedFrames, refreshRate);
                    }
                }
            }
        });
    }
}

3.2 掉帧等级划分与上报触发

SceneFrameCollectItem负责单一场景的帧率统计,按配置阈值划分掉帧等级(如正常、中等、严重、冻结),并在达到时间间隔后触发上报:

ini 复制代码
// 源码位置:com.tencent.matrix.trace.tracer.FrameTracer.java
@RequiresApi(Build.VERSION_CODES.N)
private class SceneFrameCollectItem {
    private final long[] durations = new long[FrameDuration.values().length]; // 各阶段耗时累计
    private final int[] dropLevel = new int[DropStatus.values().length]; // 掉帧等级计数
    private final int[] dropSum = new int[DropStatus.values().length]; // 掉帧次数累计
    private float dropCount; // 总掉帧数
    private float refreshRate; // 平均刷新率
    private float totalDuration; // 平均总耗时
    private long beginMs; // 统计开始时间
    private String lastScene; // 最后一次场景名
    private int count = 0; // 统计帧数
    private ISceneFrameListener listener; // 关联的监听器

    public SceneFrameCollectItem(ISceneFrameListener listener) {
        this.listener = listener;
    }

    // 追加帧数据
    public void append(String scene, FrameMetrics frameMetrics, float droppedFrames, float refreshRate) {
        // 跳过首帧(部分场景首帧渲染耗时异常)
        if ((listener.skipFirstFrame() && frameMetrics.getMetric(FrameMetrics.FIRST_DRAW_FRAME) == 1)
                || droppedFrames < (refreshRate / 60) * listener.getThreshold()) {
            return;
        }

        if (count == 0) {
            beginMs = SystemClock.uptimeMillis(); // 初始化统计开始时间
        }

        // 1. 累计各阶段耗时
        for (int i = FrameDuration.UNKNOWN_DELAY_DURATION.ordinal(); 
             i <= FrameDuration.TOTAL_DURATION.ordinal(); i++) {
            durations[i] += frameMetrics.getMetric(FrameDuration.indices[i]);
        }
        // 2. 累计掉帧数与刷新率
        dropCount += droppedFrames;
        this.refreshRate += refreshRate;
        // 3. 累计总耗时(取实际耗时与理想帧间隔的最大值)
        float frameIntervalNanos = Constants.TIME_SECOND_TO_NANO / refreshRate;
        totalDuration += Math.max(frameMetrics.getMetric(FrameMetrics.TOTAL_DURATION), frameIntervalNanos);
        // 4. 统计掉帧等级
        collect(Math.round(droppedFrames));
        count++;
        lastScene = scene;

        // 5. 达到统计间隔,触发上报并重置
        if (SystemClock.uptimeMillis() - beginMs >= listener.getIntervalMs()) {
            tryCallBackAndReset();
        }
    }

    // 划分掉帧等级(按配置的阈值:frozenThreshold>highThreshold>middleThreshold>normalThreshold)
    private void collect(int droppedFrames) {
        if (droppedFrames >= frozenThreshold) {
            dropLevel[DropStatus.DROPPED_FROZEN.ordinal()]++;
            dropSum[DropStatus.DROPPED_FROZEN.ordinal()] += droppedFrames;
        } else if (droppedFrames >= highThreshold) {
            dropLevel[DropStatus.DROPPED_HIGH.ordinal()]++;
            dropSum[DropStatus.DROPPED_HIGH.ordinal()] += droppedFrames;
        } else if (droppedFrames >= middleThreshold) {
            dropLevel[DropStatus.DROPPED_MIDDLE.ordinal()]++;
            dropSum[DropStatus.DROPPED_MIDDLE.ordinal()] += droppedFrames;
        } else if (droppedFrames >= normalThreshold) {
            dropLevel[DropStatus.DROPPED_NORMAL.ordinal()]++;
            dropSum[DropStatus.DROPPED_NORMAL.ordinal()] += droppedFrames;
        } else {
            dropLevel[DropStatus.DROPPED_BEST.ordinal()]++;
            dropSum[DropStatus.DROPPED_BEST.ordinal()] += Math.max(droppedFrames, 0);
        }
    }

    // 触发上报并重置统计数据
    void tryCallBackAndReset() {
        if (count > 20) { // 过滤样本量过小的情况
            // 计算平均值
            dropCount /= count;
            this.refreshRate /= count;
            totalDuration /= count;
            for (int i = 0; i < durations.length; i++) {
                durations[i] /= count;
            }
            // 通知监听器上报(如AllSceneFrameListener)
            listener.onFrameMetricsAvailable(
                lastScene, durations, dropLevel, dropSum,
                dropCount, this.refreshRate, Constants.TIME_SECOND_TO_NANO / totalDuration
            );
        }
        reset(); // 重置累计数据
    }

    private void reset() {
        dropCount = 0;
        refreshRate = 0;
        totalDuration = 0;
        count = 0;
        Arrays.fill(durations, 0);
        Arrays.fill(dropLevel, 0);
        Arrays.fill(dropSum, 0);
    }
}

4. 卡顿溯源:LooperMonitor 的主线程消息监控

帧率下降的本质是 "主线程被耗时任务阻塞",LooperMonitor通过 HookLooperPrinter,监听消息执行耗时,定位具体阻塞任务。

4.1 Looper Printer 替换

typescript 复制代码
// 源码位置:com.tencent.matrix.trace.core.LooperMonitor.java
private synchronized void resetPrinter() {
    Printer originPrinter = null;
    try {
        // 反射获取Looper的原生Printer(避免覆盖系统日志)
        originPrinter = ReflectUtils.get(looper.getClass(), "mLogging", looper);
        if (originPrinter == printer && null != printer) {
            return;
        }
    } catch (Exception e) {
        isReflectLoggingError = true;
        Log.e(TAG, "[resetPrinter] %s", e);
    }

    // 替换为自定义Printer,监听消息开始/结束
    looper.setMessageLogging(printer = new LooperPrinter(originPrinter));
    if (null != originPrinter) {
        MatrixLog.i(TAG, "reset printer, originPrinter[%s] in %s", 
            originPrinter, looper.getThread().getName());
    }
}

// 自定义Printer,通过系统日志格式识别消息生命周期
class LooperPrinter implements Printer {
    public Printer origin; // 原生Printer,用于转发日志
    boolean isHasChecked = false;
    boolean isValid = false; // 验证日志格式是否符合预期(">"开头为消息开始,"<"为结束)

    LooperPrinter(Printer printer) {
        this.origin = printer;
    }

    @Override
    public void println(String x) {
        // 1. 转发日志到原生Printer
        if (null != origin) {
            origin.println(x);
            if (origin == this) {
                throw new RuntimeException(TAG + " origin == this");
            }
        }

        // 2. 验证日志格式(首次调用时)
        if (!isHasChecked) {
            isValid = x.charAt(0) == '>' || x.charAt(0) == '<';
            isHasChecked = true;
            if (!isValid) {
                MatrixLog.e(TAG, "[println] Printer is inValid! x:%s", x);
            }
        }

        // 3. 分发消息开始/结束事件
        if (isValid) {
            dispatch(x.charAt(0) == '>', x);
        }
    }
}

4.2 消息耗时统计与慢消息记录

scss 复制代码
// 源码位置:com.tencent.matrix.trace.core.LooperMonitor.java
private void dispatch(boolean isBegin, String log) {
    if (isBegin) {
        // 消息开始:记录开始时间
        if (historyMsgRecorder) {
            messageStartTime = System.currentTimeMillis();
            latestMsgLog = log;
            recentMCount++;
        }
        // 通知监听器(如UIThreadMonitor)
        synchronized (oldListeners) {
            for (LooperDispatchListener listener : oldListeners) {
                if (listener.isValid()) {
                    listener.onDispatchStart(log);
                }
            }
        }
        synchronized (listeners) {
            for (DispatchListenerWrapper listener : listeners.values()) {
                if (listener.isValid()) {
                    listener.onDispatchBegin(log);
                }
            }
        }
    } else {
        // 消息结束:计算耗时并记录慢消息
        if (historyMsgRecorder) {
            long duration = System.currentTimeMillis() - messageStartTime;
            recordMsg(log, duration); // 异步记录到历史队列
        }
        // 通知监听器
        synchronized (oldListeners) {
            for (LooperDispatchListener listener : oldListeners) {
                if (listener.isValid()) {
                    listener.onDispatchEnd(log);
                }
            }
        }
        synchronized (listeners) {
            for (DispatchListenerWrapper listener : listeners.values()) {
                if (listener.isValid()) {
                    listener.onDispatchEnd(log);
                }
            }
        }
    }
}

// 异步记录慢消息(避免阻塞主线程)
private void recordMsg(final String log, final long duration) {
    historyMsgHandler.post(new Runnable() {
        @Override
        public void run() {
            enqueueHistoryMQ(new M(log, duration)); // 记录到ANR历史队列
        }
    });
    if (denseMsgTracer) {
        historyMsgHandler.post(new Runnable() {
            @Override
            public void run() {
                enqueueRecentMQ(new M(log, duration)); // 记录到近期消息队列
            }
        });
    }
}

核心逻辑 :当FrameTracer检测到帧率下降时,LooperMonitor已记录下主线程所有消息的耗时,可快速定位到 "耗时超过 16.6ms 的任务"(如主线程网络请求、复杂 JSON 解析),实现 "帧率异常→卡顿任务" 的溯源。

5. 数据上报:AllSceneFrameListener 上报监听

AllSceneFrameListener是默认的上报监听器,按固定间隔(如 5 秒)聚合全场景帧率数据,构建Issue并上报到 Matrix 监控平台:

typescript 复制代码
// 源码位置:com.tencent.matrix.trace.tracer.FrameTracer.java
@RequiresApi(Build.VERSION_CODES.N)
static class AllSceneFrameListener implements ISceneFrameListener {
    @Override
    public int getIntervalMs() {
        return Constants.DEFAULT_FPS_TIME_SLICE_ALIVE_MS; // 默认5000ms(5秒)
    }

    @Override
    public String getName() { return null; } // 未指定场景,监控所有界面

    @Override
    public boolean skipFirstFrame() { return false; }

    @Override
    public int getThreshold() { return 0; } // 不过滤掉帧数据

    @Override
    public void onFrameMetricsAvailable(@NonNull String sceneName, long[] avgDurations, 
                                        int[] dropLevel, int[] dropSum, float avgDroppedFrame, 
                                        float avgRefreshRate, float avgFps) {
        MatrixLog.i(TAG, "[report] FPS:%s scene:%s", avgFps, sceneName);
        try {
            // 获取TracePlugin实例,用于上报Issue
            TracePlugin plugin = Matrix.with().getPluginByClass(TracePlugin.class);
            if (null == plugin) return;

            // 构建JSON上报数据
            JSONObject dropLevelObject = new JSONObject();
            JSONObject dropSumObject = new JSONObject();
            for (DropStatus dropStatus : DropStatus.values()) {
                dropLevelObject.put(dropStatus.name(), dropLevel[dropStatus.ordinal()]);
                dropSumObject.put(dropStatus.name(), dropSum[dropStatus.ordinal()]);
            }

            JSONObject resultObject = new JSONObject();
            DeviceUtil.getDeviceInfo(resultObject, plugin.getApplication()); // 设备信息
            resultObject.put(SharePluginInfo.ISSUE_SCENE, sceneName); // 当前场景
            resultObject.put(SharePluginInfo.ISSUE_DROP_LEVEL, dropLevelObject); // 掉帧等级
            resultObject.put(SharePluginInfo.ISSUE_DROP_SUM, dropSumObject); // 掉帧次数
            resultObject.put(SharePluginInfo.ISSUE_FPS, avgFps); // 平均帧率
            // 追加各渲染阶段耗时
            for (FrameDuration frameDuration : FrameDuration.values()) {
                resultObject.put(frameDuration.name(), avgDurations[frameDuration.ordinal()]);
                if (frameDuration.equals(FrameDuration.TOTAL_DURATION)) break;
            }
            resultObject.put("DROP_COUNT", Math.round(avgDroppedFrame)); // 平均掉帧数
            resultObject.put("REFRESH_RATE", (int) avgRefreshRate); // 屏幕刷新率

            // 构建Issue并上报
            Issue issue = new Issue();
            issue.setTag(SharePluginInfo.TAG_PLUGIN_FPS);
            issue.setContent(resultObject);
            plugin.onDetectIssue(issue);
        } catch (JSONException e) {
            MatrixLog.e(TAG, "json error", e);
        }
    }
}

三、FrameTracer 的亮点价值

  1. 跨版本兼容性 :低版本用UIThreadMonitor+Choreographer保证基础监控,高版本用FrameMetrics增强细粒度分析,覆盖 API 16 + 所有设备;
  2. 低侵入性 :通过反射 Hook 系统 API(而非修改源码)。异步处理数据(如SceneFrameCollectorMatrixHandlerThread),避免影响应用性能;
  3. 场景化与可追溯 :按Activity隔离数据,结合LooperMonitor定位卡顿任务,实现 "从帧率异常到代码级根因" 的闭环;
  4. 动态适配:自动识别设备刷新率(60Hz/90Hz/120Hz),动态调整理想帧间隔,过滤异常数据,提升监控准确性。

以上是Matrix FrameTracer对Android 帧率监控的实现与源码剖析。欢迎关注公众号度熊君,一起分享交流。

相关推荐
DASXSDW5 小时前
NET性能优化-使用RecyclableBuffer取代RecyclableMemoryStream
java·算法·性能优化
国科安芯5 小时前
ASP3605电源芯片的性能优化与改进思路
网络·单片机·嵌入式硬件·安全·性能优化
TechMix5 小时前
【性能优化】帧率优化方法:第一步——量化
性能优化
lang201509286 小时前
MySQL 8.0性能优化终极指南
数据库·mysql·性能优化
TDengine (老段)2 天前
TDengine 时序函数 MAVG 用户手册
大数据·数据库·物联网·性能优化·时序数据库·iot·tdengine
心.c2 天前
一套完整的前端“白屏”问题分析与解决方案(性能优化)
前端·javascript·性能优化·html
qq_252924192 天前
PHP 8.0+ 极限性能优化与系统级编程
开发语言·性能优化·php
我真的是大笨蛋2 天前
开闭原则详解(OCP)
java·设计模式·性能优化·开闭原则·设计规范
189228048613 天前
NX482NX486美光固态闪存NX507NX508
大数据·网络·数据库·人工智能·性能优化