[Matrix系列-3]: TracePlugin 之 FrameTrace 源码分析

本文详细分析了Android中的FrameTrace类,探讨了其触发时机,包括TracePlugin的start()方法、FrameTrace的startTrace()方法以及LooperObserver类的各个方法。文章还深入解析了FrameTrace的数据处理过程,尤其是doFrame方法中的掉帧数计算和数据处理逻辑。通过这些分析,揭示了FrameTrace如何监控并评估UI渲染的流畅性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

开篇

第二篇文章中,我们分析了 TracePlugin 中的 LooperAnrTrace 类。今天这篇文章接着分析 TracePlugin 中的 FrameTrace 类源码。

一、FrameTrace的触发时机

1.1 TracePlugin. start() 方法

注意看,在 TracePlugin 类的start() 方法中,调用了所有 Trace 类的 onStartTrace() 方法:

@Override
public void start() {
    // ... 省略
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
             
             // ... 省略
            // 1, 通过反射得到addInputQueue method对象,加入inputCallback事件 
            UIThreadMonitor.getMonitor().onStart();
            if (traceConfig.isAnrTraceEnable()) {
                looperAnrTracer.onStartTrace();
            }

            if (traceConfig.isIdleHandlerEnable()) {
                idleHandlerLagTracer = new IdleHandlerLagTracer(traceConfig);
                idleHandlerLagTracer.onStartTrace();
            }

            if (traceConfig.isSignalAnrTraceEnable()) {
                if (!SignalAnrTracer.hasInstance) {
                    signalAnrTracer = new SignalAnrTracer(traceConfig);
                    signalAnrTracer.onStartTrace();
                }
            }

            if (traceConfig.isMainThreadPriorityTraceEnable()) {
                threadPriorityTracer = new ThreadPriorityTracer();
                threadPriorityTracer.onStartTrace();
            }
            // 2,这里调用,如果isFPSEnable为true 则启用fps监听
            if (traceConfig.isFPSEnable()) {
                frameTracer.onStartTrace();
            }

            if (traceConfig.isEvilMethodTraceEnable()) {
                evilMethodTracer.onStartTrace();
            }

            if (traceConfig.isStartupEnable()) {
                startupTracer.onStartTrace();
            }


        }
    };
    // ... 省略
} 
  • 说明:

由于在第一篇启动分析已经详细分析过,因此就省略了部分不相关的代码。我们主要看其中的两点:

  1. UIThreadMonitor.getMonitor().onStart();
  2. frameTracer.onStartTrace();

我们先看第一点到底做了什么工作。

1.1.1 UIThreadMonitor.onStart()

@Override
public synchronized void onStart() {
    if (!isInit) {
        MatrixLog.e(TAG, "[onStart] is never init.");
        return;
    }
    if (!isAlive) {
        this.isAlive = true;
        synchronized (this) {
            MatrixLog.i(TAG, "[onStart] callbackExist:%s %s", Arrays.toString(callbackExist), Utils.getStack());
            callbackExist = new boolean[CALLBACK_LAST + 1];
        }
        //1,这个int[]数组用来保存UI渲染一帧中的三个阶段(input、animation、traversal)的开始和结束状态。
        queueStatus = new int[CALLBACK_LAST + 1];
        //2,这个long[]数组则是保存每一帧的三个阶段每个阶段对应的执行耗时
        queueCost = new long[CALLBACK_LAST + 1];
        //3,往input类型的链表中加入一个input阶段的callback。
        addFrameCallback(CALLBACK_INPUT, this, true);
    }
} 
  • 说明:

1,2 两点比较好理解,见注释。第三点需要详细分析下,先看 addFrameCallback 的源码:

private synchronized void addFrameCallback(int type, Runnable callback, boolean isAddHeader) {
   //省略...
    try {
        //1, 还记得,第一篇文章中启动分析里面,通过反射拿到的choreographer中锁对象。
        synchronized (callbackQueueLock) {
            Method method = null;
            switch (type) {
                case CALLBACK_INPUT:
              
                    method = addInputQueue;
                    break;
                case CALLBACK_ANIMATION:
                    method = addAnimationQueue;
                    break;
                case CALLBACK_TRAVERSAL:
                    method = addTraversalQueue;
                    break;
            }
            // 2,最终method就是 CallbackQueue的 addCallbackLocked 方法, 反射调用callback加入type对应的链表结构中
            if (null != method) {
                method.invoke(callbackQueues[type], !isAddHeader ? SystemClock.uptimeMillis() : -1, callback, null);
                callbackExist[type] = true;
            }
        }
    } catch (Exception e) {
        MatrixLog.e(TAG, e.toString());
    }
} 

总结:

外部需要监听每一帧的不同阶段,那么就通过反射的方法对象,注册不同类型的 callback 进去即可。

可以看到,我们在 onStart() 方法中就注册了一个input类型的callback。后面肯定会走run方法。因此我们接着看下 callback 的run 方法:

@Override
public void run() {
    final long start = System.nanoTime();
    try {
        doFrameBegin(token);
        doQueueBegin(CALLBACK_INPUT);

        addFrameCallback(CALLBACK_ANIMATION, new Runnable() {

            @Override
            public void run() {
                doQueueEnd(CALLBACK_INPUT);
                doQueueBegin(CALLBACK_ANIMATION);
            }
        }, true);

        addFrameCallback(CALLBACK_TRAVERSAL, new Runnable() {

            @Override
            public void run() {
                doQueueEnd(CALLBACK_ANIMATION);
                doQueueBegin(CALLBACK_TRAVERSAL);
            }
        }, true);

    } finally {
        if (config.isDevEnv()) {
            MatrixLog.d(TAG, "[UIThreadMonitor#run] inner cost:%sns", System.nanoTime() - start);
        }
    }
} 
private void doQueueBegin(int type) {
    queueStatus[type] = DO_QUEUE_BEGIN;
    queueCost[type] = System.nanoTime();
}

private void doQueueEnd(int type) {
    queueStatus[type] = DO_QUEUE_END;
    // 计算耗时
    queueCost[type] = System.nanoTime() - queueCost[type];
    synchronized (this) {
        callbackExist[type] = false;
    }
} 
  • 说明:

逻辑很清晰,我们按顺序分别在input、animation、traversal三个阶段的开始和结束打点,然后统计每个阶段的耗时,存起来用作后面的上报。

好了,我们在接着看第二点: frameTracer.onStartTrace();

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值