Matrix源码分析之 FrameTracer 工作原理

在分析 FrameTracer 之前,还是先介绍一下他整个工作流程中的角色,加快对后续源码的理解

FrameTracer 中针对帧数据的获取存在两种方式

一 API >=24 ,直接使用 activity.getWindow().addOnFrameMetricsAvailableListener 注册回调的方式获取

1:Application.ActivityLifecycleCallbacks 全局Activity 生命周期状态监听

每当Activity生命周期状态发生改变,都会回调此函数,

2:HashSet listeners = new HashSet<>(); FrameTracer 中的全局变量,顶层事件分发者

listeners 记录着FrameTracer 中注册的所有监听,有着承上启下的作用, 当获取帧数据时遍历Set ,使用 IFrameListener 向下分发事件

3.SceneFrameCollector sceneFrameCollector; FrameTracer 中的全局变量,事件分发的中间件

下层事件的分发者,每次都会被注册进 HashSet listeners 这个Set 中,将顶层事件在这里做更细致的拆分

4:SceneFrameCollectItem 数据过滤者,

SceneFrameCollector 接收到数据后,会遍历内部的 SceneFrameCollectItem,将当前帧数据添加进入去,在这里进行过滤

5: AllSceneFrameListener 帧监测事件的末端

AllSceneFrameListener 的作用就是上报不合格数据

开始分析

从上面给出的各个角色我们大致可以猜测 FrameTracer 的大致工作流程,

开始工作时使用 Application.ActivityLifecycleCallbacks 监听 Activity 的生命周期,在合适的位置添加一个 getWindow().addOnFrameMetricsAvailableListener ,在回调后使用 顶层事件分发器将所有事件向下分发给listeners 中的 IFrameListener, IFrameListener 收到事件后根据查询条件将事件流转给 SceneFrameCollectItem , SceneFrameCollectItem 会根据设定好的条件将事件过滤,查找出丢帧的情况,将事件给到 AllSceneFrameListener 发送事件,

根据这个猜测的流程我们开始分析代码

scss 复制代码
@Override
public void onAlive() {
    super.onAlive();
    if (config.isFPSEnable()) {
        forceEnable();
    }
}
public void forceEnable() {
    MatrixLog.i(TAG, "forceEnable");
    if (sdkInt >= Build.VERSION_CODES.N) {
        Matrix.with().getApplication().registerActivityLifecycleCallbacks(this);
        sceneFrameCollector = new SceneFrameCollector();
        addListener(sceneFrameCollector);
        register(new AllSceneFrameListener());
    } else {
        UIThreadMonitor.getMonitor().addObserver(looperObserver);
    }
}

这段代码就是开始监听事件的过程, 先获取到 Application 然后利用 ActivityLifecycleCallbacks 监听Activity 的生命周期方法,我们来继续跟踪一下他组装整个回调链路的过程

java 复制代码
@RequiresApi(Build.VERSION_CODES.N)
public void addListener(IFrameListener listener) {
    synchronized (listeners) {
        listeners.add(listener);
    }
}

@RequiresApi(Build.VERSION_CODES.N)
public void register(ISceneFrameListener listener) {
    if (sceneFrameCollector != null) {
        sceneFrameCollector.register(listener);
    }
}

@RequiresApi(Build.VERSION_CODES.N)
private class SceneFrameCollector implements IFrameListener {
    private final HashMap<String, SceneFrameCollectItem> specifiedSceneMap = new HashMap<>();
    private final HashMap<ISceneFrameListener, SceneFrameCollectItem> unspecifiedSceneMap = new HashMap<>();

    public synchronized void register(@NonNull ISceneFrameListener listener) {
        if (listener.getIntervalMs() < 1 || listener.getThreshold() < 0) {
            MatrixLog.e(TAG, "Illegal value, intervalMs=%d, threshold=%d, activity=%s",
                    listener.getIntervalMs(), listener.getThreshold(), listener.getClass().getName());
            return;
        }
        String scene = listener.getName();
        SceneFrameCollectItem collectItem = new SceneFrameCollectItem(listener);
        if (scene == null || scene.isEmpty()) {
            unspecifiedSceneMap.put(listener, collectItem);
        } else {
            specifiedSceneMap.put(scene, collectItem);
        }
    }
}

从上面代码中可以看出来, addListener 就是将 创建的 SceneFrameCollector 添加到 我们在前面介绍的 HashSet 中, 而 register 就是将 AllSceneFrameListener 放入到 SceneFrameCollector 中,在 SceneFrameCollector 的 register 方法中将 AllSceneFrameListener 使用 SceneFrameCollectItem 包裹一下,那么事件如果到了 SceneFrameCollector 后,就必须经过 SceneFrameCollectItem 先来处理,同时根据他的 Name 的情况来判断添加到哪一个 HashMap 中

我们再来看看帧事件的流转的过程,也就是根据activity 的生命周期添加与移除监听

ini 复制代码
@RequiresApi(Build.VERSION_CODES.N)
@Override
public void onActivityResumed(Activity activity) {
    // 先判断这个activity是否已经被添加进去了,如果已经被监听了,那么不继续向下执行
    if (frameListenerMap.containsKey(activity.hashCode())) {
        return;
    }

    defaultRefreshRate = getRefreshRate(activity.getWindow());
    MatrixLog.i(TAG, "default refresh rate is %dHz", (int) defaultRefreshRate);
    
    // 创建回调
    Window.OnFrameMetricsAvailableListener onFrameMetricsAvailableListener = new Window.OnFrameMetricsAvailableListener() {
        private float cachedRefreshRate = defaultRefreshRate;
        private float cachedThreshold = dropFrameListenerThreshold / 60f * cachedRefreshRate;
        private int lastModeId = -1;
        private int lastThreshold = -1;
        private WindowManager.LayoutParams attributes = null;

        private void updateRefreshRate(Window window) {
            if (attributes == null) {
                attributes = window.getAttributes();
            }
            if (attributes.preferredDisplayModeId != lastModeId || lastThreshold != dropFrameListenerThreshold) {
                lastModeId = attributes.preferredDisplayModeId;
                lastThreshold = dropFrameListenerThreshold;
                cachedRefreshRate = getRefreshRate(window);
                cachedThreshold = dropFrameListenerThreshold / 60f * cachedRefreshRate;
            }
        }

        @RequiresApi(api = Build.VERSION_CODES.O)
        @Override
        public void onFrameMetricsAvailable(Window window, FrameMetrics frameMetrics, int dropCountSinceLastInvocation) {
            if (isForeground()) {
                // skip not available metrics.
                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) {
                        // some devices will produce outliers, especially the Honor series, eg: NTH-AN00, ANY-AN00, etc.
                        return;
                    }
                }
                FrameMetrics frameMetricsCopy = new FrameMetrics(frameMetrics);

                updateRefreshRate(window);

                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;

                if (dropFrameListener != null && droppedFrames >= cachedThreshold) {
                    dropFrameListener.onFrameMetricsAvailable(ProcessUILifecycleOwner.INSTANCE.getVisibleScene(), frameMetricsCopy, droppedFrames, cachedRefreshRate);
                }
                synchronized (listeners) {
                    for (IFrameListener observer : listeners) {
                        observer.onFrameMetricsAvailable(ProcessUILifecycleOwner.INSTANCE.getVisibleScene(), frameMetricsCopy, droppedFrames, cachedRefreshRate);
                    }
                }
            }
        }
    };

    this.frameListenerMap.put(activity.hashCode(), onFrameMetricsAvailableListener);
    activity.getWindow().addOnFrameMetricsAvailableListener(onFrameMetricsAvailableListener, MatrixHandlerThread.getDefaultHandler());
    MatrixLog.i(TAG, "onActivityResumed addOnFrameMetricsAvailableListener");
}

这段代码就是在 onActivityResumed 中调用的, 同时会向 activity 的widow 中添加一个 addOnFrameMetricsAvailableListener,并在 onFrameMetricsAvailableListener 组建初步的数据,使用对象锁 遍历 角色2中的 HashSet ,也就是我们创建的 SceneFrameCollector,将初步的事件交给他处理,我们再继续看一下 SceneFrameCollector 中是如何处理的

java 复制代码
private class SceneFrameCollector implements IFrameListener {

    @Override
    public void onFrameMetricsAvailable(final String sceneName, final FrameMetrics frameMetrics, final float droppedFrames, final float refreshRate) {
        frameHandler.post(new Runnable() {
            @Override
            public void run() {
                String scene = sceneName.getClass().getName();
                synchronized (SceneFrameCollector.this) {
                    SceneFrameCollectItem collectItem = specifiedSceneMap.get(scene);
                    if (collectItem != null) {
                        collectItem.append(sceneName, frameMetrics, droppedFrames, refreshRate);
                    }
                    for (SceneFrameCollectItem frameCollectItem : unspecifiedSceneMap.values()) {
                        frameCollectItem.append(sceneName, frameMetrics, droppedFrames, refreshRate);
                    }
                }
            }
        });
    }
}

在 SceneFrameCollector 的 onFrameMetricsAvailable 的回调中使用类锁,根据他的名字先去回调 specifiedSceneMap, 在遍历执行 unspecifiedSceneMap 中的 SceneFrameCollectItem ,并指定他的append 方法中

接下来再看看 SceneFrameCollectItem 的append 方法

ini 复制代码
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];
    
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();
    }
    // 记录每一个事件事件时间节点的时间                                           
    for (int i = FrameDuration.UNKNOWN_DELAY_DURATION.ordinal(); i <= FrameDuration.TOTAL_DURATION.ordinal(); i++) {
        durations[i] += frameMetrics.getMetric(FrameDuration.indices[i]);
    }
    // GPU 的时间需要单独来计算的
    if (sdkInt >= Build.VERSION_CODES.S) {
        durations[FrameDuration.GPU_DURATION.ordinal()] += frameMetrics.getMetric(FrameMetrics.GPU_DURATION);
    }

    dropCount += droppedFrames;
    // 收集当前帧的情况
    collect(Math.round(droppedFrames));
    this.refreshRate += refreshRate;
    float frameIntervalNanos = Constants.TIME_SECOND_TO_NANO / refreshRate;
    totalDuration += Math.max(frameMetrics.getMetric(FrameMetrics.TOTAL_DURATION), frameIntervalNanos);
    ++count;

    lastScene = scene;
    // 如果超过阈值,则触发上报,并且重新开始记录,默认是每10秒就会重新记录一次
    if (SystemClock.uptimeMillis() - beginMs >= listener.getIntervalMs()) {
        tryCallBackAndReset();
    }
}

这段就是判断丢帧的主要逻辑了,为什么要遍历给 durations[i] 赋值,我们先来看看 durations 是如何定义的

arduino 复制代码
private final long[] durations = new long[FrameDuration.values().length];

他是一个 数组,这个数据的长度就是 FrameDuration 这个枚举类中的变量个数,每个元素总记录的是一个long 类型的数据,每个long 的含义大致如下

ini 复制代码
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};

从运行的角度来说就是记录一下当前帧数据的各个节点的耗时情况, 将所有帧各个事件节点都添加进去

还有一个比较重要的地方就是他的收集当前帧是在哪一个区间,是冻结 or 深度丢帧 or 中度丢帧 or 普通丢帧 or 正常帧 ,那就是 collect

ini 复制代码
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);
    }
}

这里还是使用数组来记录的,不同的情况记录在不同的下标下,并将丢了多少帧都记录下来

接下来就是根据他的收集周期,周而复始的重新添加数据,他的默认收集周期是 10s

java 复制代码
public static final int DEFAULT_FPS_TIME_SLICE_ALIVE_MS = 10 * 1000;
@Override
public int getIntervalMs() {
    return Constants.DEFAULT_FPS_TIME_SLICE_ALIVE_MS;
}

到达了收集周期就需要上报并且将数据初始化 ,也就是 tryCallBackAndReset 这个方法

ini 复制代码
void tryCallBackAndReset() {
    if (count > 20) {
        dropCount /= count;
        this.refreshRate /= count;
        totalDuration /= count;
        for (int i = 0; i < durations.length; i++) {
            durations[i] /= count;
        }
        listener.onFrameMetricsAvailable(lastScene, durations, dropLevel, dropSum,
                dropCount, this.refreshRate, Constants.TIME_SECOND_TO_NANO / totalDuration);
    }
    reset();
}

这里也没有太多操作,就是计算了一下数据,向 AllSceneFrameListener 抛数据去上报,然后reset

到了这里整个 FrameTracer 在 API >= 24 的工作流程就分析完成了,接下来我们在继续分析一下他19~23的工作流程

二: 24=API>=19 利用编舞者 Choreographer 中的 callback 数组执行速度来获取帧数据

在分析Matrix 的 收集帧数据之前,我们需要先了解一下他是如何收集的帧数据,以及Matrix 内使用的这种方式是否可以行

在android 中是否丢帧是根据 Choreographer 接收到垂直同步信号的时间与上一次处理垂直同步信号的时间差来确定的,下面我们来看一下比较关键代码

ini 复制代码
public final class Choreographer {

   void doFrame(long frameTimeNanos, int frame) {
       final long startNanos;
       synchronized (mLock) {
           if (!mFrameScheduled) {
               return; // no work to do
           }

           if (DEBUG_JANK && mDebugPrintNextFrameTimeDelta) {
               mDebugPrintNextFrameTimeDelta = false;
               Log.d(TAG, "Frame time delta: "
                       + ((frameTimeNanos - mLastFrameTimeNanos) * 0.000001f) + " ms");
           }

           long intendedFrameTimeNanos = frameTimeNanos;
           startNanos = System.nanoTime();
           final long jitterNanos = startNanos - frameTimeNanos;
           // 注释1: 这里就是计算丢帧的代码
           if (jitterNanos >= mFrameIntervalNanos) {
               final long skippedFrames = jitterNanos / mFrameIntervalNanos;
               if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
                   Log.i(TAG, "Skipped " + skippedFrames + " frames!  "
                           + "The application may be doing too much work on its main thread.");
               }
               final long lastFrameOffset = jitterNanos % mFrameIntervalNanos;
               if (DEBUG_JANK) {
                   Log.d(TAG, "Missed vsync by " + (jitterNanos * 0.000001f) + " ms "
                           + "which is more than the frame interval of "
                           + (mFrameIntervalNanos * 0.000001f) + " ms!  "
                           + "Skipping " + skippedFrames + " frames and setting frame "
                           + "time to " + (lastFrameOffset * 0.000001f) + " ms in the past.");
               }
               frameTimeNanos = startNanos - lastFrameOffset;
           }

           if (frameTimeNanos < mLastFrameTimeNanos) {
               if (DEBUG_JANK) {
                   Log.d(TAG, "Frame time appears to be going backwards.  May be due to a "
                           + "previously skipped frame.  Waiting for next vsync.");
               }
               scheduleVsyncLocked();
               return;
           }

           if (mFPSDivisor > 1) {
               long timeSinceVsync = frameTimeNanos - mLastFrameTimeNanos;
               if (timeSinceVsync < (mFrameIntervalNanos * mFPSDivisor) && timeSinceVsync > 0) {
                   scheduleVsyncLocked();
                   return;
               }
           }

           mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);
           mFrameScheduled = false;
           mLastFrameTimeNanos = frameTimeNanos;
       }
           
       try {
           //注释2: 这里就是每一帧 Choreographer 都做了哪些工作,我们只要知道他开始工作的时间,与他结束工作的时间,就知道了一帧用了多长时间
           Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
           AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);

           mFrameInfo.markInputHandlingStart();
           doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);

           mFrameInfo.markAnimationsStart();
           doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
           doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos);

           mFrameInfo.markPerformTraversalsStart();
           doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);

           doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
       } finally {
           AnimationUtils.unlockAnimationClock();
           Trace.traceEnd(Trace.TRACE_TAG_VIEW);
       }

       if (DEBUG_FRAMES) {
           final long endNanos = System.nanoTime();
           Log.d(TAG, "Frame " + frame + ": Finished, took "
                   + (endNanos - startNanos) * 0.000001f + " ms, latency "
                   + (startNanos - frameTimeNanos) * 0.000001f + " ms.");
       }
   }
}

关于 Choreographer 还是有不少内容的,大家可以看一下我之前的博客 # Choreographer 编舞者的艺术

根据注释2 的提示,我们只需向 Choreographer 的 mCallbackQueues 中添加我们的事件即可,但是改如何巧妙的向他们中添加数据呢,下面就开始分析 Matrix 的方案

开始分析

还是从这段代码开始

scss 复制代码
@Override
public void onAlive() {
    super.onAlive();
    if (config.isFPSEnable()) {
        forceEnable();
    }
}

public void forceEnable() {
    MatrixLog.i(TAG, "forceEnable");
    if (sdkInt >= Build.VERSION_CODES.N) {
        Matrix.with().getApplication().registerActivityLifecycleCallbacks(this);
        sceneFrameCollector = new SceneFrameCollector();
        addListener(sceneFrameCollector);
        register(new AllSceneFrameListener());
    } else {
        UIThreadMonitor.getMonitor().addObserver(looperObserver);
    }
}

只不过我们这次需要跟踪的是 UIThreadMonitor.getMonitor

先来分析一下他的init 方法

typescript 复制代码
public void init(TraceConfig config) {
    this.config = config;

    if (Thread.currentThread() != Looper.getMainLooper().getThread()) {
        throw new AssertionError("must be init in main thread!");
    }

    boolean historyMsgRecorder = config.historyMsgRecorder;
    boolean denseMsgTracer = config.denseMsgTracer;

    LooperMonitor.register(new LooperMonitor.LooperDispatchListener(historyMsgRecorder, denseMsgTracer) {
        @Override
        public boolean isValid() {
            return isAlive;
        }

        @Override
        public void dispatchStart() {
            super.dispatchStart();
            UIThreadMonitor.this.dispatchBegin();
        }

        @Override
        public void dispatchEnd() {
            super.dispatchEnd();
            UIThreadMonitor.this.dispatchEnd();
        }

    });
    this.isInit = true;
    choreographer = Choreographer.getInstance();
    frameIntervalNanos = ReflectUtils.reflectObject(choreographer, "mFrameIntervalNanos", Constants.DEFAULT_FRAME_DURATION);
    callbackQueueLock = ReflectUtils.reflectObject(choreographer, "mLock", new Object());
    callbackQueues = ReflectUtils.reflectObject(choreographer, "mCallbackQueues", null);
    if (null != callbackQueues) {
        addInputQueue = ReflectUtils.reflectMethod(callbackQueues[CALLBACK_INPUT], ADD_CALLBACK, long.class, Object.class, Object.class);
        addAnimationQueue = ReflectUtils.reflectMethod(callbackQueues[CALLBACK_ANIMATION], ADD_CALLBACK, long.class, Object.class, Object.class);
        addTraversalQueue = ReflectUtils.reflectMethod(callbackQueues[CALLBACK_TRAVERSAL], ADD_CALLBACK, long.class, Object.class, Object.class);
    }
    vsyncReceiver = ReflectUtils.reflectObject(choreographer, "mDisplayEventReceiver", null);

    MatrixLog.i(TAG, "[UIThreadMonitor] %s %s %s %s %s %s frameIntervalNanos:%s", callbackQueueLock == null, callbackQueues == null,
            addInputQueue == null, addTraversalQueue == null, addAnimationQueue == null, vsyncReceiver == null, frameIntervalNanos);

    if (config.isDevEnv()) {
        addObserver(new LooperObserver() {
            @Override
            public void doFrame(String focusedActivity, long startNs, long endNs, boolean isVsyncFrame, long intendedFrameTimeNs, long inputCostNs, long animationCostNs, long traversalCostNs) {
                MatrixLog.i(TAG, "focusedActivity[%s] frame cost:%sms isVsyncFrame=%s intendedFrameTimeNs=%s [%s|%s|%s]ns",
                        focusedActivity, (endNs - startNs) / Constants.TIME_MILLIS_TO_NANO, isVsyncFrame, intendedFrameTimeNs, inputCostNs, animationCostNs, traversalCostNs);
            }
        });
    }
}

这里使用到了 LooperMonitor ,当 LooperMonitor 的 start 调用自己的start ,LooperMonitor 的end 来调用自己的end ,由于开篇就是 FrameTracer 开始写的,并没有关于LooperMonitor 的介绍,我们先来大致的说一下 LooperMonitor 的工作原理,以及 为什么使用它,

LooperMonitor 就是使用的 Looper setMessageLogging 方法,来给Looper 设置一个监听,每次 Looper 在开始处理消息和处理结束时都会回调我们设置的 Printer,那么我们就知道他开始处理某一个消息,和结束处理某一个消息了, 我们知道整个android系统的刷新就是基于Looper 的消息机制,我们只需要在开始时使用反射向 Choreographer 的 mCallbackQueues[0] 的链表的头部添加一个回调,当这个回调被调用时就代表着整个绘制帧的操作开始了,此时标记整个消息为垂直同步消息,那么我们就知道这个消息执行的开始时间与结束时间,如果我们想要获得更为准确的 mCallbackQueues 数组中哪种类型的时间耗时,可以在 mCallbackQueues[0] 添加的回调中,向你想要的的 mCallbackQueues 中各个节点添加头尾数据,就是更为精确的各个事件的执行时间

讲完了 LooperMonitor 在整个收集帧数据的过程中的作用,我们继续来分析代码,在init 方法中就是创建了 LooperMonitor 开始处理与结束处理的回调,并且反射了 Choreographer 中的变量,方便后续在使用过程中再去反射,

我们从 UIThreadMonitor 的 onStart 开始节点开始分析

ini 复制代码
@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];
        }
        queueStatus = new int[CALLBACK_LAST + 1];
        queueCost = new long[CALLBACK_LAST + 1];
        addFrameCallback(CALLBACK_INPUT, this, true);
    }
}

在存活后,开始向 Choreographer 的 mCallbackQueues[0] 中添加一个头数据,

ini 复制代码
private synchronized void addFrameCallback(int type, Runnable callback, boolean isAddHeader) {
    if (callbackExist[type]) {
        MatrixLog.w(TAG, "[addFrameCallback] this type %s callback has exist! isAddHeader:%s", type, isAddHeader);
        return;
    }

    if (!isAlive && type == CALLBACK_INPUT) {
        MatrixLog.w(TAG, "[addFrameCallback] UIThreadMonitor is not alive!");
        return;
    }
    try {
        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;
            }
            if (null != method) {
                method.invoke(callbackQueues[type], !isAddHeader ? SystemClock.uptimeMillis() : -1, callback, null);
                callbackExist[type] = true;
            }
        }
    } catch (Exception e) {
        MatrixLog.e(TAG, e.toString());
    }
}

在添加过程中需要一个Runnable ,也就是执行的回调,而开始时间是将 UIThreadMonitor 他本身作为回调传入进去的,我们继续分析 UIThreadMonitor 的run 方法

scss 复制代码
@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);
        }
    }
}

在我们插入的Choreographer 的 mCallbackQueues 中的 CALLBACK_INPUT 事件开始执行后,会向 mCallbackQueues[CALLBACK_ANIMATION] 添加一个动画监听,这个动画监听的回调会标记 CALLBACK_INPUT 结束,以及 CALLBACK_ANIMATION 开始, 还会使用同样的原理向 mCallbackQueues 添加一个 CALLBACK_TRAVERSAL 事件,并且使用回调记录整个过程的事件,

当所有的 mCallbackQueues 事件执行完毕就代表着本次Looper 处理完成了当前这个Message ,也就会回调 LooperMonitor 的 dispatchEnd ,并调用 UIThreadMonitor 的 dispatchEnd 方法,我们看一下他的代码

ini 复制代码
private void dispatchEnd() {
    long traceBegin = 0;
    if (config.isDevEnv()) {
        traceBegin = System.nanoTime();
    }

    if (config.isFPSEnable()) {
        long startNs = token;
        long intendedFrameTimeNs = startNs;
        // 如果是处理的帧数据,那么则执行下面代码
        if (isVsyncFrame) {
            doFrameEnd(token);
            intendedFrameTimeNs = getIntendedFrameTimeNs(startNs);
        }

        long endNs = System.nanoTime();

        synchronized (observers) {
            for (LooperObserver observer : observers) {
                if (observer.isDispatchBegin()) {
                    observer.doFrame(AppActiveMatrixDelegate.INSTANCE.getVisibleScene(), startNs, endNs, isVsyncFrame, intendedFrameTimeNs, queueCost[CALLBACK_INPUT], queueCost[CALLBACK_ANIMATION], queueCost[CALLBACK_TRAVERSAL]);
                }
            }
        }
    }

    if (config.isEvilMethodTraceEnable() || config.isDevEnv()) {
        dispatchTimeMs[3] = SystemClock.currentThreadTimeMillis();
        dispatchTimeMs[1] = System.nanoTime();
    }

    AppMethodBeat.o(AppMethodBeat.METHOD_ID_DISPATCH);

    synchronized (observers) {
        for (LooperObserver observer : observers) {
            if (observer.isDispatchBegin()) {
                observer.dispatchEnd(dispatchTimeMs[0], dispatchTimeMs[2], dispatchTimeMs[1], dispatchTimeMs[3], token, isVsyncFrame);
            }
        }
    }

    this.isVsyncFrame = false;

    if (config.isDevEnv()) {
        MatrixLog.d(TAG, "[dispatchEnd#run] inner cost:%sns", System.nanoTime() - traceBegin);
    }
}    

当我们向 Choreographer 的 mCallbackQueues 插入的input 事件回调后,会使用一个flag 来标记当前Message 是否是帧数据的消息,这个标记就是 isVsyncFrame ,在 dispatchEnd 被调用后,如果 isVsyncFrame 为true ,就会触发 doFrameEnd

arduino 复制代码
private void doFrameEnd(long token) {

    doQueueEnd(CALLBACK_TRAVERSAL);

    for (int i : queueStatus) {
        if (i != DO_QUEUE_END) {
            queueCost[i] = DO_QUEUE_END_ERROR;
            if (config.isDevEnv) {
                throw new RuntimeException(String.format("UIThreadMonitor happens type[%s] != DO_QUEUE_END", i));
            }
        }
    }
    queueStatus = new int[CALLBACK_LAST + 1];

    addFrameCallback(CALLBACK_INPUT, this, true);

}

在 doFrameEnd 中 标记 CALLBACK_TRAVERSAL 的执行结束时间,并重新向 Choreographer 中添加一个 CALLBACK_INPUT 重复的收集帧数据

到了这里整个 FrameTracer 的工作原理就分析完成了

相关推荐
Python私教5 分钟前
Python ORM 框架 SQLModel 快速入门教程
android·java·python
编程乐学1 小时前
基于Android Studio 蜜雪冰城(奶茶饮品点餐)—原创
android·gitee·android studio·大作业·安卓课设·奶茶点餐
problc2 小时前
Android中的引用类型:Weak Reference, Soft Reference, Phantom Reference 和 WeakHashMap
android
IH_LZH2 小时前
Broadcast:Android中实现组件及进程间通信
android·java·android studio·broadcast
去看全世界的云2 小时前
【Android】Handler用法及原理解析
android·java
机器之心3 小时前
o1 带火的 CoT 到底行不行?新论文引发了论战
android·人工智能
机器之心3 小时前
从架构、工艺到能效表现,全面了解 LLM 硬件加速,这篇综述就够了
android·人工智能
AntDreamer3 小时前
在实际开发中,如何根据项目需求调整 RecyclerView 的缓存策略?
android·java·缓存·面试·性能优化·kotlin
运维Z叔5 小时前
云安全 | AWS S3存储桶安全设计缺陷分析
android·网络·网络协议·tcp/ip·安全·云计算·aws
Reese_Cool6 小时前
【C语言二级考试】循环结构设计
android·java·c语言·开发语言