请选择 进入手机版 | 继续访问电脑版

Android 腾讯 Matrix 原理分析(三):TracePlugin 卡顿分析之帧率监听

[复制链接]
谭先生 发表于 2021-1-2 17:49:33 | 显示全部楼层 |阅读模式 打印 上一主题 下一主题
文章目次



前言

TracePlugin 卡顿分析插件中包罗许多 Tracer,而 FrameTracer 负责监听帧率。拿到产生的帧率数据之后,根据用户设置的丢帧阈值举行陈诉。
那么 TracePlugin 是怎么拿到每一帧数据的?本篇文章将围绕这个问题依据 Matrix 源码举行解答。
一、准备工作

我在上篇文章中写道,UIThreadMonitor 是 Tracer 工作功能实现的根本,FrameTracer 也不例外。
  UIThreadMonitor 可以监听主线程 Looper 事件、吸收硬件每 16ms 发送来的垂直同步 VSync 信号,而 FrameTracer 通过设置监听到 UIThreadMonitor 吸收到每帧刷新的回调方法 onFrame() 。
从 FrameTracer 的先容我们知道,该类具有统计每帧数据的本事,而这些数据则是由 UIThreadMonitor 提供的。UIThreadMonitor 又是怎么做到的呢?接下来一起看看吧。
1.1 UIThreadMonitor 初始化

TracePlugin 在 Matrix init 之后由开辟者手动启动,TracePlugin 启动后会初始化 UIThreadMonitor,执行它的 init() 方法:
  UIThreadMonitor # init()
  1. public class UIThreadMonitor implements BeatLifecycle, Runnable {                private static final String ADD_CALLBACK = "addCallbackLocked";    private Object callbackQueueLock;    private Object[] callbackQueues;    // 三种范例添加数据的方法    private Method addTraversalQueue;    private Method addInputQueue;    private Method addAnimationQueue;    private Choreographer choreographer;        public void init(TraceConfig config) {        if (Thread.currentThread() != Looper.getMainLooper().getThread()) {            throw new AssertionError("must be init in main thread!");        }        // 第一部分                 // getInstance() 是 ThreadLocal 实现,在主线程创建的,所以获取的是主线程的 Choreographer        choreographer = Choreographer.getInstance();        // 获取 Choreographer 的对象锁        callbackQueueLock = ReflectUtils.reflectObject(choreographer, "mLock", new Object());        // 获取 Choreographer 的 mCallbackQueues 对象,也就是 CallbackQueues 数组        callbackQueues = ReflectUtils.reflectObject(choreographer, "mCallbackQueues", null);        if (null != callbackQueues) {                // 获取三种范例对象的 addCallbackLocked 方法            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);        frameIntervalNanos = ReflectUtils.reflectObject(choreographer, "mFrameIntervalNanos", Constants.DEFAULT_FRAME_DURATION);    }        // 第二部分    LooperMonitor.register(new LooperMonitor.LooperDispatchListener() {         @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() 是 ThreadLocal 实现,而 UIThreadMonitor 是在主线程初始化的,所以获取的是主线程的 Choreographer 对象。
Choreographer 在吸收到 VSync 信号之后会通过内部的 Handler 发送一个异步消息执行 doFrame() 方法。由于是主线程的 Choreographer,所以执行 doFrame() 方法也是在主线程情况下完成的。
而在 doFrame() 方法中会遍历 CallbackQueue 数组(Choreographer 内部维护的回调队列)并回调它们的 doFrame()/run() 方法,Matrix 固然可以通过 Choreographer 的外部方法向 CallbackQueue 添加监听,但是这样做就只能吸收到回调而不能到达监控的目标。
所以 Matrix 使用反射向 CallbackQueue 数组每个下标链表头部添加回调,在每个范例回调之后更新状态并统计耗时。
回过头来看第一部分的代码,第一部分要注意的是反射获取的三个方法的逻辑:


  • callbackQueues 数组三个下标对应三种差异的回调范例对象:CALLBACK_INPUT(下标 0)、CALLBACK_ANIMATION(下标 1)、CALLBACK_TRAVERSAL(下标 2)。
  • 那么 callbackQueues[CALLBACK_INPUT]、callbackQueues[CALLBACK_ANIMATION]、callbackQueues[CALLBACK_TRAVERSAL] 获取的就是这三种范例的对象实例。
  • 最后获取它们的 addCallbackLocked() 方法,这个方法的作用是往当前链表添加元素。
接着看一下反射获取到的方法原型:
  Choreographer.CallbackQueue # addCallbackLocked()
  1. public void addCallbackLocked(long dueTime, Object action, Object token) {        // 缓存获取,如果没有则创建    CallbackRecord callback = obtainCallbackLocked(dueTime, action, token);    CallbackRecord entry = mHead;    // 头结点为 null 直接替代    if (entry == null) {        mHead = callback;        return;    }    // 根据时间排序    if (dueTime < entry.dueTime) {        callback.next = entry;        mHead = callback;        return;    }    while (entry.next != null) {        if (dueTime < entry.next.dueTime) {            callback.next = entry.next;            break;        }        entry = entry.next;    }    entry.next = callback;}
复制代码


  • CallbackRecord:所有的回调会被包装为 CallbackRecord 类,该类会生存定时时间、回调、token,后文会再分析;
  • 反面的逻辑也比力简朴,就是往当前链表添加元素。注意如果传入的定时时间 dueTime 小于头结点的时间,则会替换头结点。
这样就 创建了 Choreographer 并提供了添加三种回调范例的方法。
第二部分:监听主线程 Looper 事件前后

第二部分的代码主要就是监听主线程 Looper 处理处罚每条消息前后,也就是说处理处罚消息前回调 dispatchStart()、消息被 Handler 处理处罚之后回调 dispatchEnd()。
有关 UIThreadMonitor 和 LooperMonitor 的实现可以参考前文:
  Android 腾讯 Matrix 原理分析(二):TracePlugin 卡顿分析之主线程监听
2.2 准备数据容器

UIThreadMonitor 执行 init() 初始化之后,紧接着会调用 onStar() 方法启动:
  UIThreadMonitor # onStar()
  1. public class UIThreadMonitor implements BeatLifecycle, Runnable {        // 三种回调三个下标        public static final int CALLBACK_INPUT = 0;        public static final int CALLBACK_ANIMATION = 1;        public static final int CALLBACK_TRAVERSAL = 2;        // 回调的最大值        private static final int CALLBACK_LAST = CALLBACK_TRAVERSAL;        // 创建数组存放状态和泯灭时间        private int[] queueStatus = new int[CALLBACK_LAST + 1];        private long[] queueCost = new long[CALLBACK_LAST + 1];                @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.状态数组                queueStatus = new int[CALLBACK_LAST + 1];                // 2.泯灭时间数组                queueCost = new long[CALLBACK_LAST + 1];                addFrameCallback(CALLBACK_INPUT, this, true);            }        }}
复制代码
根据上篇文章可知,我们需要关注的回调范例有三种:CALLBACK_INPUT(输入)CALLBACK_ANIMATION(动画)CALLBACK_TRAVERSAL(绘制)
对于 Matrix 来说,要做的事情有两件:

  • 记载每种回调的状态,也就是数组 queueStatus,容量为 3(因为监听三种回调嘛)。
    顺带提一下,数组 queueStatus 每个下标可以赋值为固定的状态值:
  1. private static final int DO_QUEUE_BEGIN = 1;private static final int DO_QUEUE_END = 2;
复制代码

  • 记载每种回调所泯灭的时间,数组 queueCost,容量同样为 3。
    每个下标第一次记载回调开始的时间,第二次盘算出泯灭的时间并记载。
这样添加监听系统信号的方法有了,储存数据的容器也有了,就可以让 FrameTracer 添加监听并吸收数据了。
二、FrameTracer 添加监听

FrameTracer 是 TracePlugin(卡顿分析插件) 的一部分,所以也是在 TracePlugin 中创建和开始工作的:
  TracePlugin # init & start() 大略版
  1. public class TracePlugin extends Plugin {        private FrameTracer frameTracer;        @Override        public void init(Application app, PluginListener listener) {            super.init(app, listener);            // 1.初始化            frameTracer = new FrameTracer(traceConfig);        }                @Override        public void start() {        super.start();    Runnable runnable = new Runnable() {         @Override         public void run() {             if (!UIThreadMonitor.getMonitor().isInit()) {                 try {                     UIThreadMonitor.getMonitor().init(traceConfig);                 } catch (java.lang.RuntimeException e) {                     MatrixLog.e(TAG, "[start] RuntimeException:%s", e);                     return;                 }             }             // 开始监听主线程             UIThreadMonitor.getMonitor().onStart();             // 2.Tracer 开始工作             frameTracer.onStartTrace();         }     };        }}
复制代码

  • new FrameTracer:FrameTracer 的构造器主要使用传入的用户设置 traceConfig 举行参数设置,然后添加一个 FPS 的监听。后文大概还会分析,这里先不贴这段代码。
  • frameTracer.onStartTrace():TracePlugin 的 start() 方法是由开辟者手动调用的,内里调用父类 Tracer 的onStartTrace() 标记 FrameTracer 进入运动状态。
  Tracer # onStartTrace()
  1. @Overridefinal synchronized public void onStartTrace() {    if (!isAlive) {        this.isAlive = true;        onAlive();    }}
复制代码
紧接着调用 onAlive() 使子类 FrameTracer 开始工作:
  FrameTracer # onAlive()
  1. @Overridepublic void onAlive() {    super.onAlive();    UIThreadMonitor.getMonitor().addObserver(this);}
复制代码
可以看到 FrameTracer 进入运动状态后只是把自己添加到 UIThreadMonitor 的监听者列表中,添加的范例是 LooperObserver。
  UIThreadMonitor # addObserver
  1. public void addObserver(LooperObserver observer) {    if (!isAlive) {        onStart();    }    synchronized (observers) {        observers.add(observer);    }}
复制代码
因为父类 Tracer 实现了 LooperObserver 接口,所以可以被添加到监听列表中,而添加监听的目标就是为了吸收每帧回调。
在第一节的准备中,UIThreadMonitor 已经拥有了监听系统 VSync 信号的本事,只需要在吸收到信号的时候回调这些监听就可以让 FrameTracer 吸收到每帧的回调。
接下来看 UIThreadMonitor 是如何吸收系统垂直同步信号并返回给监听者的。
三、UIThreadMonitor 监听帧率

3.1 监听系统 VSync 信号

UIThreadMonitor 的 onStart() 方法中有一句重要代码:
  UIThreadMonitor # onStart()
  1. @Overridepublic 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);    }}
复制代码
就是最后这句 addFrameCallback(CALLBACK_INPUT, this, true);,注意此时第一个参数为 CALLBACK_INPUT 体现添加输入范例的回调、第二个参数 this 也就是把 UIThreadMonitor 这个线程对象通报、第三个 true 体现添加到队首。
接下来看是怎么添加的:
  UIThreadMonitor # addFrameCallback()
  1. 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;            // 1. 根据添加范例得到要调用的对象方法            switch (type) {                case CALLBACK_INPUT:                    method = addInputQueue;                    break;                case CALLBACK_ANIMATION:                    method = addAnimationQueue;                    break;                case CALLBACK_TRAVERSAL:                    method = addTraversalQueue;                    break;            }            if (null != method) {                    // 2. 调用相应对象添加元素的方法                method.invoke(callbackQueues[type], !isAddHeader ? SystemClock.uptimeMillis() : -1, callback, null);                callbackExist[type] = true;            }        }    } catch (Exception e) {        MatrixLog.e(TAG, e.toString());    }}
复制代码

  • 首先根据传入的回调范例确定调用的方法,方法对象在 init() 的时候已经创建好了,这里直接调用就可以了。
    比如这里传入的是 CALLBACK_INPUT 范例,使用的是 addInputQueue 方法对象。
  1. Method addInputQueue = ReflectUtils.reflectMethod(callbackQueues[CALLBACK_INPUT], ADD_CALLBACK, long.class, Object.class, Object.class);
复制代码
这个 Method 对象由三部分组成:


  • callbackQueues[CALLBACK_INPUT]:对象实例,获取 callbackQueues 第一个元素,范例为 CallbackQueue;
  • ADD_CALLBACK:方法名,体现调用的是上面临象的 addCallbackLocked 方法;
    private static final String ADD_CALLBACK = "addCallbackLocked";
  • long.class, Object.class, Object.class:方法参数,对象大概包罗多个方法重载,所以传入入参范例确定详细调用哪个方法。
  CallbackQueue # addCallbackLocked
public void addCallbackLocked(long dueTime, Object action, Object token) {...}

  • 确定好方法之后,就可以调用 invoke() 执行了,反面参数也可以分为三部分:


  • callbackQueues[type]:对象实例,传来的是 CALLBACK_INPUT 也就是调用 callbackQueues 第一个对象的 addCallbackLocked() 方法;
  • !isAddHeader ? SystemClock.uptimeMillis() : -1:如果添加到链表头传 -1,反之传入开机到当前的时间总数;
  • callback:吸收系统信号的回调,这里传入的是 this 体现由 UIThreadMonitor 吸收。UIThreadMonitor 是一个线程,所以会回调它的 run() 方法。
回调添加完毕,接下来就等系统发出 VSync 信号了。
3.3 准备吸收系统信号

ViewRootImp 在首次绘制大概子 View 们发生厘革时会请求吸收 VSync 信号,接着 Choreographer 就会收到硬件发来的信号。
Choreographer 收到系统的 VSync 信号之后,调用 doFrame() 遍历回调数组:
  Choreographer # doFrame()
  1. void doFrame(long frameTimeNanos, int frame) {           ...     try {         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);         mFrameInfo.markPerformTraversalsStart();         doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);         doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);     } finally {         AnimationUtils.unlockAnimationClock();         Trace.traceEnd(Trace.TRACE_TAG_VIEW);     }     ... }
复制代码
这段代码要注意的是回调执行的顺序,可以看到先是回调 CALLBACK_INPUT 范例的数据,比及遍历回调完毕之后再遍历后续的 CALLBACK_ANIMATIONCALLBACK_TRAVERSAL
而回调的方法也很简朴,就是生成 CallbackRecord 链表并遍历执行 run() 方法:
  Choreographer # doCallbacks()
  1. void doCallbacks(int callbackType, long frameTimeNanos) {        CallbackRecord callbacks;    synchronized (mLock) {        final long now = System.nanoTime();        // 1.遍历 callbackType 范例链表,包装成 CallbackRecord 链表        callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(                now / TimeUtils.NANOS_PER_MS);        if (callbacks == null) {            return;        }        mCallbacksRunning = true;                ...    }    try {        Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]);        // 2.遍历执行 run 方法        for (CallbackRecord c = callbacks; c != null; c = c.next) {            c.run(frameTimeNanos);        }    } finally {        synchronized (mLock) {            mCallbacksRunning = false;            do {                final CallbackRecord next = callbacks.next;                // 接纳资源                recycleCallbackLocked(callbacks);                callbacks = next;            } while (callbacks != null);        }        Trace.traceEnd(Trace.TRACE_TAG_VIEW);    }}
复制代码
CallbackRecord 的 run() 方法就是根据 token 来判定执行 doFrame() 照旧 run()。
  Choreographer.CallbackRecord
  1. private static final class CallbackRecord {    public CallbackRecord next;    public long dueTime;    public Object action; // Runnable or FrameCallback    public Object token;        // 调用 run 方法    public void run(long frameTimeNanos) {        if (token == FRAME_CALLBACK_TOKEN) {            ((FrameCallback)action).doFrame(frameTimeNanos);        } else {            ((Runnable)action).run();        }    }}
复制代码
在 3.3 节中可知 UIThreadMonitor 传入的 token 是 null,所以会回调它的 run()方法。
3.4 吸收到信号之后

  UIThreadMonitor # run()
  1. @Overridepublic void run() {    final long start = System.nanoTime();    try {            // 1.标记回调垂直同步        doFrameBegin(token);        // 2.记载时间        doQueueBegin(CALLBACK_INPUT);                // 3.添加动画范例回调        addFrameCallback(CALLBACK_ANIMATION, new Runnable() {            @Override            public void run() {                    // 3.1CALLBACK_INPUT竣事、CALLBACK_ANIMATION开始                doQueueEnd(CALLBACK_INPUT);                doQueueBegin(CALLBACK_ANIMATION);            }        }, true);                // 4.添加绘制范例回调        addFrameCallback(CALLBACK_TRAVERSAL, new Runnable() {            @Override            public void run() {                    // 4.1CALLBACK_ANIMATION竣事、CALLBACK_TRAVERSAL开始                doQueueEnd(CALLBACK_ANIMATION);                doQueueBegin(CALLBACK_TRAVERSAL);            }        }, true);    } finally {        if (config.isDevEnv()) {            MatrixLog.d(TAG, "[UIThreadMonitor#run] inner cost:%sns", System.nanoTime() - start);        }    }}private void doFrameBegin(long token) {        // 垂直同步标记    this.isVsyncFrame = true;}
复制代码

  • doFrameBegin() 方法只是设置一个标记,用来体现当前吸收到了垂直同步信号,该标记反面会有用处;
  • 执行到 run() 方法说明 Choreographer 已经开始回调 CALLBACK_INPUT 范例的对象了,而 UIThreadMonitor 又被添加到了链表头部,所以记载时间作为 CALLBACK_INPUT 范例回调的起始;
  1. private void doQueueBegin(int type) {        // 记载状态    queueStatus[type] = DO_QUEUE_BEGIN;    // 记载时间    queueCost[type] = System.nanoTime();}
复制代码

  • 接着添加 CALLBACK_ANIMATION 范例的回调,等候 Choreographer 的遍历;
    3.1 比及开始回调 CALLBACK_ANIMATION 范例的数据,说明 CALLBACK_INPUT 已经全部回调完毕了,调用 doQueueEnd() 更新状态记载时间。
  1. private void doQueueEnd(int type) {        // 更新状态    queueStatus[type] = DO_QUEUE_END;    // 当前时间 - 开始时记载的时间 = 处理处罚 type 范例泯灭的时间    queueCost[type] = System.nanoTime() - queueCost[type];    synchronized (this) {        callbackExist[type] = false;    }}
复制代码

  • 同理,添加 CALLBACK_TRAVERSAL 范例的回调;
    4.1 比及开始执行 CALLBACK_TRAVERSAL 范例的回调,说明上一个状态的回调已经遍历执行完毕了,所以更改 CALLBACK_ANIMATION 的状态并记载所泯灭的时间。
小总结

这部分大概稍微有点绕,我们画个图来资助明白:


  • UIThreadMonitor 添加 CALLBACK_INPUT 范例的回调到 Choreographer;
    addFrameCallback(CALLBACK_INPUT, this, true);
  • Choreographer 吸收到系统信号,遍历 CALLBACK_INPUT 链表并执行回调;
    doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
  • 此时 UIThreadMonitor 吸收到回调,说明 CALLBACK_INPUT 范例的事件开始处理处罚了,记载为开始状态并记载开始时间;
    doQueueBegin(CALLBACK_INPUT);
    然后添加 CALLBACK_ANIMATION 范例的回调;
    addFrameCallback(CALLBACK_ANIMATION, new Runnable(){...},true);
  • Choreographer 开始遍历 CALLBACK_ANIMATION 链表,然后执行回调;
    doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
  • CALLBACK_ANIMATION 范例吸收到回调,说明上一个范例 CALLBACK_INPUT 事件处理处罚完毕了,记载为竣事状态并统计耗时;
    doQueueEnd(CALLBACK_INPUT);
    同时 CALLBACK_ANIMATION 事件开始处理处罚了,记载为开始状态并记载开始时间;
    doQueueBegin(CALLBACK_ANIMATION);
  • 添加 CALLBACK_TRAVERSAL 范例的回调;
    addFrameCallback(CALLBACK_TRAVERSAL, new Runnable(){...},true);
  • Choreographer 开始遍历 CALLBACK_TRAVERSAL 链表,然后执行回调;
    doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
  • CALLBACK_TRAVERSAL 范例吸收到回调,说明 上一个范例CALLBACK_ANIMATION 的事件处理处罚完毕了,记载为竣事状态并统计耗时;
    doQueueEnd(CALLBACK_ANIMATION);
    同时 CALLBACK_TRAVERSAL 事件开始处理处罚了,记载为开始状态并记载开始时间;
到这里 ViewRootImp 三种范例的回调都已经执行了,UIThreadMonitor 也乐成监听并统计耗时。但是尚有最后的问题,CALLBACK_TRAVERSAL 回调只有开始没有竣事。
要办理这个问题,需要相识一个大前提:三种范例的回调是在 Choreographer 的 doFrame() 方法中回调的,而 doFrame() 方法是由 Handler 发送线程来执行的。
  Choreographer.FrameDisplayEventReceiver
  1. private final class FrameDisplayEventReceiver extends DisplayEventReceiver        implements Runnable {    ...    @Override    public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {        ...        Message msg = Message.obtain(mHandler, this);        msg.setAsynchronous(true);        mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);    }    @Override    public void run() {        doFrame(mTimestampNanos, mFrame);    }}
复制代码
Handler 发送的事件是由 Looper 取出来处理处罚的,如果可以或许监听 Looper 处理处罚完这个事件了,说明 doFrame() 方法也执行完毕、 CALLBACK_TRAVERSA 也回调完毕了。
那么这个 Looper 处理处罚事件竣事时可以监听到么?固然可以,就是上篇文章分析过的 LooperMonitor。
四、FrameTracer 吸收数据

LooperMonitor 是在 UIThreadMonitor 初始化时添加监听的:
  UIThreadMonitor # init()
  1. public void init(TraceConfig config) {        ...    LooperMonitor.register(new LooperMonitor.LooperDispatchListener() {        @Override        public boolean isValid() {            return isAlive;        }        @Override        public void dispatchStart() {            super.dispatchStart();            UIThreadMonitor.this.dispatchBegin();        }        @Override        public void dispatchEnd() {            super.dispatchEnd();            UIThreadMonitor.this.dispatchEnd();        }    });    ...}
复制代码
这样就可以或许监听到 Looper 取出来的所有事件处理处罚前后的消息,固然前提是在主线程情况下。
因为 UIThreadMonitor 是在主线程创建的,所以监听的就是主线程的 Looper。
但是这里尚有一个问题,主线程 Looper 所有事件都监听,怎么才气确定是 VSync 信号事件呢?很简朴,在吸收到 VSync 信号的第一个回调设置一个标记,就是前面出现过的 isVsyncFrame
可以看到 UIThreadMonitor 在吸收到第一个回调之后,就标记 isVsyncFrame 为 true 说明现在处理处罚的是 doFrame() 事件。
  UIThreadMonitor # run()
  1. @Overridepublic void run() {    final long start = System.nanoTime();    try {        doFrameBegin(token);        ...    }}private void doFrameBegin(long token) {    this.isVsyncFrame = true;}
复制代码
然后在 doFrame() 事件竣事之后,给 LooperMonitor 设置的监听会回调 dispatchEnd() 方法执行 UIThreadMonitor 的 dispatchEnd() 方法:
  UIThreadMonitor # dispatchEnd()
  1. private void dispatchEnd() {    long traceBegin = 0;    long startNs = token;    long intendedFrameTimeNs = startNs;    if (isVsyncFrame) {            // 1.竣事回调        doFrameEnd(token);        intendedFrameTimeNs = getIntendedFrameTimeNs(startNs);    }    // 2.通知监听    long endNs = System.nanoTime();    synchronized (observers) {         for (LooperObserver observer : observers) {             if (observer.isDispatchBegin()) {                 observer.doFrame(AppMethodBeat.getVisibleScene(), startNs, endNs, isVsyncFrame, intendedFrameTimeNs, queueCost[CALLBACK_INPUT], queueCost[CALLBACK_ANIMATION], queueCost[CALLBACK_TRAVERSAL]);             }         }    }    this.isVsyncFrame = false;}
复制代码

  • 可以看到如果是 isVsyncFrame doFrame() 事件则调用 doFrameEnd() 竣事 CALLBACK_TRAVERSA 事件:
  UIThreadMonitor # doFrameEnd()
  1. private void doFrameEnd(long token) {        // 标记竣事,统计耗时    doQueueEnd(CALLBACK_TRAVERSAL);    queueStatus = new int[CALLBACK_LAST + 1];        // 设置新一轮的监听    addFrameCallback(CALLBACK_INPUT, this, true);}
复制代码


  • 到这个方法就竣事了 CALLBACK_TRAVERSA 的回调,而且乐成统计到了耗时;
  • doFrame() 大概是被连续调用的,因为 UI 发生厘革每秒要刷新 60 次呢,所以需要添加新一轮的监听。
    再次吸收到 CALLBACK_INPUT 就再修改状态、统计时间,添加别的两种范例的回调。

  • 一轮 doFrame() 执行完毕之后,就可以回调添加到 UIThreadMonitor 的监听来统计信息了。而 FrameTracer 就是众多监听者的一员。
    UIThreadMonitor 遍历所有 LooperObserver 并执行它们的 doFrame() 方法并通报参数,FrameTracer 通过 doFrame() 方法就可以拿到数据展示信息了。
  FrameTracer # doFrame()
  1. @Overridepublic void doFrame(String focusedActivity, long startNs, long endNs, boolean isVsyncFrame, long intendedFrameTimeNs, long inputCostNs, long animationCostNs, long traversalCostNs) {    if (isForeground()) {        notifyListener(focusedActivity, startNs, endNs, isVsyncFrame, intendedFrameTimeNs, inputCostNs, animationCostNs, traversalCostNs);    }}
复制代码
到这里 FrameTracer 终于吸收到了数据,依据这些数据就可以举行帧率统计、帧率图绘制等工作了。
总结

简朴总结下本篇文章记载的内容:

  • FrameTracer 为了吸收三种事件处理处罚的时间和帧率,向 UIThreadMonitor 添加监听;
  • UIThreadMonitor 内部获取主线程 Choreographer 用于吸收垂直同步信号,同时反射获取向 Choreographer 数组添加回调方法的对象;
  • UIThreadMonitor 初始化时向 Choreographer 数组添加回调,等 Choreographer 回调数组之跋文录每种回调的状态和耗时;
  • 最后 UIThreadMonitor 将回调得来的数据返回给监听者,此中就包罗 FrameTracer。
  • FrameTracer 拿到数据之后在作处理处罚,鉴于篇幅,FrameTracer 的详细逻辑将在下篇文章举行分析。
为了得到 FrameTracer 要使用的数据,使用了 UIThreadMonitorLooperMonitorChoreographer 等,真是不容易呀。
对于 Matrix 来说,UIThreadMonitor 是很重要的部分。帧率、慢函数、ANR 监控等都用到了它,看懂 UIThreadMonitor 的实现对明白 Matrix 框架会有很大资助。
最后感谢各人的阅读。

来源:https://blog.csdn.net/Never_Stay/article/details/111633883
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则


专注素材教程免费分享
全国免费热线电话

18768367769

周一至周日9:00-23:00

反馈建议

27428564@qq.com 在线QQ咨询

扫描二维码关注我们

Powered by Discuz! X3.4© 2001-2013 Comsenz Inc.( 蜀ICP备2021001884号-1 )