likes
comments
collection
share

Android性能优化系列-腾讯matrix-TracePlugin卡顿优化之UIThreadMonitor源码分析

作者站长头像
站长
· 阅读数 18

前言

UIThreadMonitor也是TracePlugin中的一个基础的能力,在分析其他tracer之前,有必要先将这些基础能力理解清楚,再进行后续的分析才能事半功倍。

UIThreadMonitors实现了两个接口:BeatLifecycle、Runnable,所以它是有生命周期的,同时也能运行在子线程中。我们从他几个关键的方法入手来查看源码。

init

方法初始化时,主要做了两件事:

- Choreographer对象
- Choreographer中的锁对象:mLock
- Choreographer中的CallbackQueue数组:mCallbackQueues
- Choreographer中的FrameDisplayEventReceiver对象:vsyncReceiver
- CallbackQueue数组中CALLBACK_INPUT类型的CallbackQueue对象的addCallbackLocked方法
- CallbackQueue数组中CALLBACK_ANIMATION类型的CallbackQueue对象的addCallbackLocked方法
- CallbackQueue数组中CALLBACK_TRAVERSAL类型的CallbackQueue对象的addCallbackLocked方法

注意,在UIThreadMonitor中,useFrameMetrics我们默认认为其值为false,因为UIThreadMonitor本身就是针对useFrameMetrics为false时的处理方案

public void init(TraceConfig config, boolean supportFrameMetrics) {
    //android N及以上使用Android提供了原生方法用于获取帧刷新的信息
    //addOnFrameMetricsAvailableListener,但是细心的你会发现,matrix
    //中Android O及以上supportFrameMetrics才设置为true?
    useFrameMetrics = supportFrameMetrics;
    
    LooperMonitor.register(new LooperMonitor.LooperDispatchListener(historyMsgRecorder, denseMsgTracer) {
        ...
    });
    //mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());
    //1s = 1000000000ns, mFrameIntervalNanos表示刷新一次消耗的纳秒值,
    //假如刷新率为60,那么mFrameIntervalNanos = 16666666纳秒
    frameIntervalNanos = ReflectUtils.reflectObject(choreographer, "mFrameIntervalNanos", Constants.DEFAULT_FRAME_DURATION);
    if (!useFrameMetrics) {
        //8.0以下,反射得到Choreographer中的几个关键对象的方法
        choreographer = Choreographer.getInstance();
        callbackQueueLock = ReflectUtils.reflectObject(choreographer, "mLock", new Object());
        //反射得到mCallbackQueues数组
        callbackQueues = ReflectUtils.reflectObject(choreographer, "mCallbackQueues", null);
        if (null != callbackQueues) {
            //反射得到mCallbackQueues数组中不同类型对象的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);
        }
        //choreographer中的FrameDisplayEventReceiver对象,用于接收vsync信号
        vsyncReceiver = ReflectUtils.reflectObject(choreographer, "mDisplayEventReceiver", null);
    }
}

onStart

初始化完成之后,开始onStart的调用,初始化了三个数组,然后调用addFrameCallback,将CALLBACK_INPUT类型的一个Runnable添加CALLBACK_INPUT类型的CallbackQueue中,并且是添加到头部位置。

@Override
public synchronized void onStart() {
    synchronized (this) {
        //用来记录指定type类型的runnable是否添加过,如添加过,不会重复添加
        callbackExist = new boolean[CALLBACK_LAST + 1];
    }
    if (!useFrameMetrics) {
        //两个数组,记录状态和时间,后边会用到
        queueStatus = new int[CALLBACK_LAST + 1];
        queueCost = new long[CALLBACK_LAST + 1];
        //往CallbackQueue中添加
        addFrameCallback(CALLBACK_INPUT, this, true);
    }
}

addFrameCallback

此方法的作用是调用初始化时反射获取到的addCallbackLocked方法:

  1. addInputQueue
  2. addAnimationQueue
  3. addTraversalQueue

这三个Method都是addCallbackLocked的方法,只不过是不同对象的addCallbackLocked。 这里最先通过反射执行的是CALLBACK_INPUT类型的CallbackQueue的addCallbackLocked方法,会将当前Runnable(UIThreadMonitors本身就实现了Runnable接口)添加到系统Choreographer中的对应type类型的CallbackQueue中。

private synchronized void addFrameCallback(int type, Runnable callback, boolean isAddHeader) {
    synchronized (callbackQueueLock) {
        Method method = null;
        switch (type) {
            //CALLBACK_INPUT类型
            case CALLBACK_INPUT:
                method = addInputQueue;
                break;
            //CALLBACK_ANIMATION类型
            case CALLBACK_ANIMATION:
                method = addAnimationQueue;
                break;
            //CALLBACK_TRAVERSAL类型
            case CALLBACK_TRAVERSAL:
                method = addTraversalQueue;
                break;
        }
        if (null != method) {
            //反射执行addCallbackLocked方法,将runnable添加进去
            method.invoke(callbackQueues[type], !isAddHeader ? SystemClock.uptimeMillis() : -1, callback, null);
        }
    }
}

addCallbackLocked

CallbackQueue是一个队列,队列内部存储的是CallbackRecord,CallbackRecord这里可以简单的认为是包装了Runnable的一个类,我们看下addCallbackLocked方法。

public void addCallbackLocked(long dueTime, Object action, Object token) {
    //将Runnable等信息封装成CallbackRecord对象
    CallbackRecord callback = obtainCallbackLocked(dueTime, action, token);
    CallbackRecord entry = mHead;
    //链表为空,设置为表头
    if (entry == null) {
        mHead = callback;
        return;
    }
    //将CallbackRecord插入到链表头位置,因为它时间比当前链表头时间小
    if (dueTime < entry.dueTime) {
        callback.next = entry;
        mHead = callback;
        return;
    }
    //将CallbackRecord插入到链表中,按照dueTime时间大小排序
    while (entry.next != null) {
        if (dueTime < entry.next.dueTime) {
            callback.next = entry.next;
            break;
        }
        entry = entry.next;
    }
    entry.next = callback;
}

那么这里在UIThreadMonitor onStart的时候往CALLBACK_INPUT类型的CallbackQueue队列中插入一个Runnable目的是什么?matrix想做什么?这里需要理解一些Choreographer的机制,简单提一下,详细内容可自行搜索。

Choreographer运行机制

Android的消息的运行依赖于VSYNC信号,每当新的VSYNC信号到达时,系统会一次性批量处理一些消息任务。而VSYNC信号的申请和接收是由Choreographer来管理的,我们直接进入VSYNC信号到达的位置看下代码,找到doFrame方法,这里只保留了与本次相关的逻辑。

void doFrame(long frameTimeNanos, int frame) {
    doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
    doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
    doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos);
    doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
    doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
}

可以看出doCallbacks传递的第一个参数似乎与上边提到的几种类型的type相呼应了,没错,它们表示的其实是同一个意思,这里提到了五种type,而matrix中关注的只有其中三种。我们看下doCallbacks的实现。

void doCallbacks(int callbackType, long frameTimeNanos) {
    CallbackRecord callbacks;
    synchronized (mLock) {
        final long now = System.nanoTime();
        //拿到对应type的CallbackRecord
        callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
                now / TimeUtils.NANOS_PER_MS);
        if (callbacks == null) {
            return;
        }
    }
    try {
        //遍历这个链表,所有被遍历到的CallbackRecord执行其run方法
        for (CallbackRecord c = callbacks; c != null; c = c.next) {
            c.run(frameTimeNanos);
        }
    } finally {
    }
}

可以看出,doCallbacks是按照当前时间从CallbackQueues中拿到一个CallbackRecord作为入口,遍历这个链表,所有被遍历到的CallbackRecord执行其run方法,这也就是上边我们提到的“VSYNC信号到达时,系统会一次性批量处理一些消息任务”。所以按照顺序,是先执行CALLBACK_INPUT类型的任务,再执行CALLBACK_ANIMATION类型的任务,下一步执行CALLBACK_TRAVERSAL类型的任务,不同类型的任务按照优先级依次排队执行。

run

继续回到上边对addFrameCallback的分析中,在onStart的时候将一个Runnable插入到CALLBACK_INPUT类型的CallbackQueues队列的最靠前的位置,那么当下一次VSYNC信号到来的时候,这个Runnable会在所有CALLBACK_INPUT类型的任务中第一个被执行,于是就走到了这里的run方法。

public void run() {
    //此方法执行时,说明vsync信号到达,主线程已经开始处理input类型的第一条消息
    final long start = System.nanoTime();
    try {
        //将isVsyncFrame设置为true,token是时间戳,记录消息队列当前消息执行前的那个时间节点
        doFrameBegin(token);
        //这里用到了onStart中初始化的两个数组,用来记录CALLBACK_INPUT类型消息开始的时间
        doQueueBegin(CALLBACK_INPUT);
        //调用addFrameCallback将CALLBACK_ANIMATION类型的runnable加入CALLBACK_ANIMATION
        //类型的队列中,这样,当CALLBACK_INPUT类型的消息全部执行完时,就会执行到下面这个runnable,并将这个节点作为CALLBACK_INPUT类型消息结束的时机,记录这一vsync信号期间,
        //所有CALLBACK_INPUT类型消息执行消耗的时长。
        addFrameCallback(CALLBACK_ANIMATION, new Runnable() {

            @Override
            public void run() {
                //input类型消息执行完成
                doQueueEnd(CALLBACK_INPUT);
                //记录CALLBACK_ANIMATION开始执行
                doQueueBegin(CALLBACK_ANIMATION);
            }
        }, true);
        //调用addFrameCallback将CALLBACK_TRAVERSAL类型的runnable加入CALLBACK_TRAVERSAL
        //类型的队列中,这样,当CALLBACK_ANIMATION类型的消息全部执行完时,就会执行到下面这个runnable,并将这个节点作为CALLBACK_ANIMATION类型消息结束的时机,记录这一vsync信号期间,
        //所有CALLBACK_ANIMATION类型消息执行消耗的时长。
        addFrameCallback(CALLBACK_TRAVERSAL, new Runnable() {

            @Override
            public void run() {
                //CALLBACK_ANIMATION类型消息执行完成
                doQueueEnd(CALLBACK_ANIMATION);
                //记录CALLBACK_TRAVERSAL开始执行
                doQueueBegin(CALLBACK_TRAVERSAL);
            }
        }, true);

    } finally {
    }
}
private void doFrameEnd(long token) {
    doQueueEnd(CALLBACK_TRAVERSAL);
    //上一VSYNC周期结束了,再添加一个CALLBACK_INPUT,如此周而复始,整个消息运行
    //的时间就清晰的记录了下来
    addFrameCallback(CALLBACK_INPUT, this, true);
}
private void doQueueBegin(int type) {
    //指定type开始
    queueStatus[type] = DO_QUEUE_BEGIN;
    //记录开始的时间
    queueCost[type] = System.nanoTime();
}
private void doQueueEnd(int type) {
    //指定type结束
    queueStatus[type] = DO_QUEUE_END;
    //记录type类型消息消耗的时长
    queueCost[type] = System.nanoTime() - queueCost[type];
    synchronized (this) {
        callbackExist[type] = false;
    }
}

onStop

onStop切换状态。

@Override
public synchronized void onStop() {
    if (!isInit) {
        return;
    }
    if (isAlive) {
        this.isAlive = false;
    }
}

LooperMonitor的两个回调方法也是很关键的逻辑,这里我们顺着这两个回调再看一下相关源码。

dispatchBegin

这里会回调dispatchBegin方法给外界,传递相关参数。

dispatchEnd

此方法中会回调doFrame和dispatchEnd方法给外界,并传递相关参数。

private void dispatchEnd() {
    if (config.isFPSEnable() && !useFrameMetrics) {
        long startNs = token;
        long intendedFrameTimeNs = startNs;
        if (isVsyncFrame) {
            //记录traversal执行完成,并开启下一个周期
            doFrameEnd(token);
            intendedFrameTimeNs = getIntendedFrameTimeNs(startNs);
        }

        long endNs = System.nanoTime();

        synchronized (observers) {
            for (LooperObserver observer : observers) {
                if (observer.isDispatchBegin()) {
                   //一帧结束。回调doFrame
                   //注意餐素比较关键
                   //1.当前可见的页面
                   //2.消息执行前的时间点
                   //3.消息执行完成后的时间点
                   //4.是否是根据vsync计算
                   //5.onVsync方法回调时的时间点
                   //6.input消耗的时长
                   //7.animation消耗的时长
                   //8.traversal消耗的时长
                   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();
    }
    //主动调用o方法
    AppMethodBeat.o(AppMethodBeat.METHOD_ID_DISPATCH);
    //回调消息分发结束
    synchronized (observers) {
        for (LooperObserver observer : observers) {
            if (observer.isDispatchBegin()) {
                //参数
                //1.dispathStart开始的时间戳
                //2.线程执行(cpu执行)开始的时间
                //3.dispatchEnd结束时的时间戳
                //4.线程执行(cpu执行)结束的时间
                //5.token等于dispatchTimeMs[0]
                //4和2两个参数可以计算出cpu运行的时长,3和1可以计算出总时长
                observer.dispatchEnd(dispatchTimeMs[0], dispatchTimeMs[2], dispatchTimeMs[1], dispatchTimeMs[3], token, isVsyncFrame);
            }
        }
    }
    this.isVsyncFrame = false;
}

总结

由上边的分析可知,UIThreadMonitors总体上就是对LooperMonitor的封装与增强,它借助了LooperMonitor两个回调,dispatchBegin和dispatchEnd,并在这两个回调的基础上,丰富了各种参数,如消息执行总时间、消息执行期间cpu运行的时间,一个消息中input时间消耗的时间、animation消耗的时间、traversal消耗的时间等等,方便后续进行更细致的分析。

转载自:https://juejin.cn/post/7280793995554570294
评论
请登录