[Matrix系列-2]: TracePlugin 之 LooperAnrTrace源码分析

1,839 阅读8分钟

接着第一篇的分析,看看TracePlugin的start()方法。

一、TracePlugin的init()和start()

这个start方法是核心了,我们需要好好去分析。

在分析start方法之前,我们先回顾下tracePlugininit(Application app, PluginListener listener)方法。

1.1 init(Application app, PluginListener listener)

@Override
public void init(Application app, PluginListener listener) {
    super.init(app, listener);
    MatrixLog.i(TAG, "trace plugin init, trace config: %s", traceConfig.toString());
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
        MatrixLog.e(TAG, "[FrameBeat] API is low Build.VERSION_CODES.JELLY_BEAN(16), TracePlugin is not supported");
        unSupportPlugin();
        return;
    }
    
    looperAnrTracer = new LooperAnrTracer(traceConfig);

    frameTracer = new FrameTracer(traceConfig);

    evilMethodTracer = new EvilMethodTracer(traceConfig);

    startupTracer = new StartupTracer(traceConfig);
}

简要分析:后面再去单独分析每个tracer的逻辑。

  1. 顾名思义,封装looper的anr的具体逻辑
  2. 封装帧率 fps相关逻辑
  3. 封装慢方法逻辑
  4. 封装启动耗时逻辑

从类的设计角度来看,tracePlugin里面管理着四种不同功能的tracer。

不出意外,接下来的start方法肯定是初始化tracer和对四种tracer的生命周期管理,从而具体的逻辑切分到具体的tracer中去,符合单一原则。

1.2 start()方法

@Override
public void start() {
    super.start();
    if (!isSupported()) {
        MatrixLog.w(TAG, "[start] Plugin is unSupported!");
        return;
    }
    MatrixLog.w(TAG, "start!");
    Runnable runnable = new Runnable() {
        @Override
        public void run() {

            if (willUiThreadMonitorRunning(traceConfig)) {
                if (!UIThreadMonitor.getMonitor().isInit()) {
                    try {
                        //1, UI线程监控初始化,包含:
                        UIThreadMonitor.getMonitor().init(traceConfig);
                    } catch (java.lang.RuntimeException e) {
                        MatrixLog.e(TAG, "[start] RuntimeException:%s", e);
                        return;
                    }
                }
            }

            //2, AppMethodBeat  开启慢方法监听
            if (traceConfig.isAppMethodBeatEnable()) {
                AppMethodBeat.getInstance().onStart();
            } else {
                AppMethodBeat.getInstance().forceStop();
            }
            //3,
            UIThreadMonitor.getMonitor().onStart();
            //4, looper的 anr监控??
            if (traceConfig.isAnrTraceEnable()) {
                looperAnrTracer.onStartTrace();
            }
            //5, idleHandler监控?
            if (traceConfig.isIdleHandlerEnable()) {
                idleHandlerLagTracer = new IdleHandlerLagTracer(traceConfig);
                idleHandlerLagTracer.onStartTrace();
            }
            //6,信号anr的监控??
            if (traceConfig.isSignalAnrTraceEnable()) {
                if (!SignalAnrTracer.hasInstance) {
                    signalAnrTracer = new SignalAnrTracer(traceConfig);
                    signalAnrTracer.onStartTrace();
                }
            }
            //7,线程优先级的监控
            if (traceConfig.isMainThreadPriorityTraceEnable()) {
                threadPriorityTracer = new ThreadPriorityTracer();
                threadPriorityTracer.onStartTrace();
            }
            //8, frameTracer的start方法
            if (traceConfig.isFPSEnable()) {
                frameTracer.onStartTrace();
            }
            //9, 慢方法的start方法
            if (traceConfig.isEvilMethodTraceEnable()) {
                evilMethodTracer.onStartTrace();
            }
            // 10, 启动监控start方法
            if (traceConfig.isStartupEnable()) {
                startupTracer.onStartTrace();
            }


        }
    };

    if (Thread.currentThread() == Looper.getMainLooper().getThread()) {
        runnable.run();
    } else {
        MatrixLog.w(TAG, "start TracePlugin in Thread[%s] but not in mainThread!", Thread.currentThread().getId());
        MatrixHandlerThread.getDefaultMainHandler().post(runnable);
    }

}

简要分析:

  1. UIThreadMonitor类的init()初始化。这个类非常的重要,主要的工作是去通过反射方式去拿到Choreographer的队列和回调监听,从而实现自己的逻辑。loopMonitor则是监听looper中消息处理前后的逻辑,后面具体分析。

  2. 修改AppMethodBeat中的状态为STATUS_STARTED,目的是isAlive()返回true,表示AppMethodBeat的io方法处于激活状态了。

  3. 调用UIThreadMonitorstart方法 后面具体分析

  4. 4、5、6、7、8、9、10最终都会调用到每个tracer对应的onAlive()方法,后面具体分析~

至此,初始化调用流程分析完毕,我们知道,tracePlugin最终的功能都体现在了每种不同类型的tracer中。因此,提出两个问题:

  1. 每种不同的tracer的具体是怎么实现的?实现的原理是?
  2. 还要理清楚tracePlugin到底是如何管理这些tracer的?类间的关系?

但是,同时需要解决另一个疑惑: UIThreadMonitorAppMethodBeat是用来干什么的? 不搞清楚这个问题,无法继续分析。。。。

二、UIThreadMonitor和AppMethodBeat

2.1 UIThreadMonitor

这个类的职责:

  • 反射拿到Choreographer类中的事件队列callbackQueues,方便外部直接加入事件
  • 通过LooperMonitor对looper中mLooging进行替换

2.1.1 init(TraceConfig config)方法

public void init(TraceConfig config) {
    if (Thread.currentThread() != Looper.getMainLooper().getThread()) {
        throw new AssertionError("must be init in main thread!");
    }
    this.config = config;
    //1, 获取Choreographer编舞者对象
    choreographer = Choreographer.getInstance();
    //2,反射得到 Choreographer 的mLock锁对象
    callbackQueueLock = ReflectUtils.reflectObject(choreographer, "mLock", new Object());
       //3,反射得到 Choreographer 的callbackQueues[]。
    callbackQueues = ReflectUtils.reflectObject(choreographer, "mCallbackQueues", null);
    if (null != callbackQueues) {
    // 4, 根据input、animation、traversal类型来,反射Choreographer 的 addCallbackLocked方法,得到method对象。方便后续分别加入callback到不同的链表中。
        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);
    }
    //5,反射得到vsync接收器
    vsyncReceiver = ReflectUtils.reflectObject(choreographer, "mDisplayEventReceiver", null);
    //5,反射得到frameIntervalNanos,默认是16.666ms,但这里是纳秒单位
    frameIntervalNanos = ReflectUtils.reflectObject(choreographer, "mFrameIntervalNanos", Constants.DEFAULT_FRAME_DURATION);
    // 6,注册监听。当looper中的消息分发开始和结束的时候会触发回调
    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;
    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()) {
    //7,如果是开发环境,则打印出log??
        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);
            }
        });
    }
}

逻辑分析:

想要看懂上面的逻辑,我们先看下Choreographer类作用,简单总结如下:

1,通过getInstance()方法返回Choreographer实例(返回的是属于当前线程的一个实例,因为用到了当前线程的ThreadLocal中的Looper来驱动,一般是主线程使用)。

2,外部通过postCallback方法(如:viewRootImpl中调用了mChoreographer.postCallback)根据类型来注册一个callback。目的是等待下一次的onVsync信号到来的时候,回调callbackrun方法。

3,callback有四类:input、animation、inserts、travelsal。分别对应屏幕的输入事件、动画执行、insert、测量+布局+绘制三大流程。 多个同类型的callback以队列的形式被存储在mCallbackQueues数组中,该数组默认长度为5。也就是说数组中的每一项都是一个队列,每一个队列都存储着注册后的callback

4,总的流程:FrameDisplayEventReceiver会向系统注册一个onVsync回调。FrameDisplayEventReceiver.onVsync--Choreographer.doFrame()--doCallback(按顺序执行所以注册的callback)。不用类型的callback执行顺序为:input>animation>inserts>travelsal

这里我对mCallbackQueues 存储结构画了一张图,有助于理解:

image.png

说明:

  • mCallbackQueues: 是一个数组,类型为 CallbackQueue。
  • CallbackQueue:封装CallbackRecord的操作:添加、移除等
  • CallbackRecord: 链表结构。存储了外部注册的callback。

有关于choreographer的详细分析,可参考Gityuan的这篇: Choreographer原理

好了,继续分析:

  1. 反射得到 Choreographer 的mLock锁对象,目的添加自己的callback的时候用来作为锁对象,保持跟系统锁对象的一致。
  2. 反射得到mCallbackQueues数组对象,用来获取具体类型的CallbackRecord中的callback队列(队列底层是通过链表实现的)
  3. 反射得到不同类型的addCallbackLocked方法,包含input、animation、travelsal,用来添加自定义的callback进去。
  4. 反射得到onVsync接收器,用来反射获取onVsync回调时候的mTimestampNanos,目的是跟系统回调同步时间。
  5. 反射得到mFrameIntervalNanos,帧间时长,一般等于16.66ms。用来计算掉帧率
  6. 通过设置Looper的mLogger对象,判断message处理开始前和结束前的回调时机,可以计算该message处理耗时。

2.1.2 LooperMonitor 类

在深入了解之前,我们先了解它的工作原理:

LooperMonitor监听的原理是利用Loopernext()会在消息处理的前后打印消息,我们只要能够监听到这两条消息之间即可计算出这条消息的耗时!

for (;;) {
    Message msg = queue.next(); // might block
    if (msg == null) {
        // No message indicates that the message queue is quitting.
        return;
    }

    // This must be in a local variable, in case a UI event sets the logger
    final Printer logging = me.mLogging;
    if (logging != null) {
    // 消息处理前,打印消息
        logging.println(">>>>> Dispatching to " + msg.target + " " +
                msg.callback + ": " + msg.what);
    }
    
    ....消息处理
    
   //消息处理后,
if (logging != null) {
    logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
    

在第6步的时候,又看到向LooperMonitor注册了回调,从而监听looper中的消息处理回调。我们有必要去搞清楚里面到底是如何做的。

public static LooperMonitor of(@NonNull Looper looper) {
    LooperMonitor looperMonitor = sLooperMonitorMap.get(looper);
    if (looperMonitor == null) {
        looperMonitor = new LooperMonitor(looper);
        sLooperMonitorMap.put(looper, looperMonitor);
    }
    return looperMonitor;
}

of静态方法可以看出,LooperMonitor不仅仅可以监听主线程还可以监听其他线程的消息卡顿时间。只要传入不同线程的looper即可。

继续看构造方法:

private LooperMonitor(Looper looper) {
    Objects.requireNonNull(looper);
    this.looper = looper;
    //代替looper中的mLogging对象
    resetPrinter();
    //往IdleHandler添加一个 外部handler,用来在空闲的时候做逻辑
    addIdleHandler(looper);
}

继续看resetPrinter()方法:

private synchronized void resetPrinter() {
    Printer originPrinter = null;
    try {
        if (!isReflectLoggingError) {
        //只要反射不报错,都会走这里。 
        //再通过反射拿到looper的mLooging对象
                originPrinter = ReflectUtils.get(looper.getClass(), "mLogging", looper);
                //与之前反射过的printer进行对比,一致则返回。一般情况下都是一致的,除非有不同classloader加载过。
            if (originPrinter == printer && null != printer) {
                return;
            }
            // Fix issues that printer loaded by different classloader
            if (originPrinter != null && printer != null) {
                if (originPrinter.getClass().getName().equals(printer.getClass().getName())) {
                    MatrixLog.w(TAG, "LooperPrinter might be loaded by different classloader"
                            + ", my = " + printer.getClass().getClassLoader()
                            + ", other = " + originPrinter.getClass().getClassLoader());
                    return;
                }
            }
        }
    } catch (Exception e) {
        isReflectLoggingError = true;
        Log.e(TAG, "[resetPrinter] %s", e);
    }

    if (null != printer) {
        MatrixLog.w(TAG, "maybe thread:%s printer[%s] was replace other[%s]!",
                looper.getThread().getName(), printer, originPrinter);
    }
    //替换looper的logging为我们自定义的printer
    looper.setMessageLogging(printer = new LooperPrinter(originPrinter));
    if (null != originPrinter) {
        MatrixLog.i(TAG, "reset printer, originPrinter[%s] in %s", originPrinter, looper.getThread().getName());
    }
}

简要分析:

里面逻辑在注释里已经写清楚了。addIdleHandler(looper)的逻辑是每次消息处理后的ideleHandler回调中没60s再去调用resetPrinter()方法,确保是我们自己的Printer对象。

最后,就是通过LooperPrinter中的println方法回调,来判断消息处理的开始和结束。从而分发给对应的监听者,实现逻辑处理。

class LooperPrinter implements Printer {
    public Printer origin;
    boolean isHasChecked = false;
    boolean isValid = false;

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

    @Override
    public void println(String x) {
        if (null != origin) {
            origin.println(x);
            if (origin == this) {
                throw new RuntimeException(TAG + " origin == this");
            }
        }

        if (!isHasChecked) {
            isValid = x.charAt(0) == '>' || x.charAt(0) == '<';
            isHasChecked = true;
            if (!isValid) {
                MatrixLog.e(TAG, "[println] Printer is inValid! x:%s", x);
            }
        }

        if (isValid) {
        //x.charAt(0) == '>': 表示消息开始
        // x.charAt(0) == '<' 表示消息结束
            dispatch(x.charAt(0) == '>', x);
        }

    }
}

2.2 AppMethodBeat

这个类是用来在App所有Java方法的的开始和结束的时候插入 i(int methodId)o(int methodId)两个方法,用来统计方法执行耗时。最终会进行聚合输出调用关系链。

trace-plugin插件会在编译时期的transform阶段,通过ASM字节码插桩技术遍历所有的class字节码文件中的方法,在每个方法的执行前后插入i/o方法,用来统计耗时。可以通过配置blackMethodList.txt白名单来排除某些方法的插桩。此外,还会通过一个自增数来给每个方法分配一个methodId与之对应,并输出到mapping文件中,方便后续堆栈解析的时候查询

三、xxxTracer的逻辑

明白了UIThreadMonitor和AppMethodBeat的作用后,我们再回到之前提出两个问题:

  1. 每种不同的tracer的具体是怎么实现的?实现的原理是?
  2. 还要理清楚tracePlugin到底是如何管理这些tracer的?类间的关系?

OK,先看LooperAnrTrace类:

3.1 LooperAnrTracer 类

1,在onAlive向UIThreadMonitor注册回调,而UIThreadMonitor中有注册了LooperMonitor的回调, 最终回调到dispatchBegindispatchEnd方法。

@Override
public void onAlive() {
    super.onAlive();
    if (isAnrTraceEnable) {
        //监听looper中消息的开始和结束事件
        UIThreadMonitor.getMonitor().addObserver(this);
        this.anrHandler = new Handler(MatrixHandlerThread.getDefaultHandler().getLooper());
        this.lagHandler = new Handler(MatrixHandlerThread.getDefaultHandler().getLooper());
    }
}

2,dispatchBegin发送了两个延迟task。 anrTask为5s,lagTask为2s。 当然会减掉上面代码的引起的误差cost。

@Override
public void dispatchBegin(long beginNs, long cpuBeginMs, long token) {
    super.dispatchBegin(beginNs, cpuBeginMs, token);
    //在anrtask中标记开始标志start,用来后面发生anr时候的用来截取sBuffer数组存储的执行过的方法。
    //
    anrTask.beginRecord = AppMethodBeat.getInstance().maskIndex("AnrTracer#dispatchBegin");
    anrTask.token = token;

    if (traceConfig.isDevEnv()) {
        MatrixLog.v(TAG, "* [dispatchBegin] token:%s index:%s", token, anrTask.beginRecord.index);
    }
    // 有个小小的疑问? 当looper被主线程阻塞的时候,这个怎么检测?
    //答案:阻塞是正常的现象。当被MessageQueue的next方法阻塞时,也不会回调dispatchBegin,从而不会触发获取堆栈逻辑。
    long cost = (System.nanoTime() - token) / Constants.TIME_MILLIS_TO_NANO;
    anrHandler.postDelayed(anrTask, Constants.DEFAULT_ANR - cost);
    lagHandler.postDelayed(lagTask, Constants.DEFAULT_NORMAL_LAG - cost);
}

3,这个比较简单,就是在dispatchEnd消息处理结束后移除之前的两个延迟task。 代码执行到这里即表示消息没有出现卡顿或者anr。

@Override
public void dispatchEnd(long beginNs, long cpuBeginMs, long endNs, long cpuEndMs, long token, boolean isBelongFrame) {
    super.dispatchEnd(beginNs, cpuBeginMs, endNs, cpuEndMs, token, isBelongFrame);
    if (traceConfig.isDevEnv()) {
        long cost = (endNs - beginNs) / Constants.TIME_MILLIS_TO_NANO;
        MatrixLog.v(TAG, "[dispatchEnd] token:%s cost:%sms cpu:%sms usage:%s",
                token, cost,
                cpuEndMs - cpuBeginMs, Utils.calculateCpuUsage(cpuEndMs - cpuBeginMs, cost));
    }
    if (null != anrTask) {
        anrTask.getBeginRecord().release();
        anrHandler.removeCallbacks(anrTask);
    }
    if (null != lagTask) {
        lagHandler.removeCallbacks(lagTask);
    }
}

4,如果这两个消息有执行到,那么证明发生了anr或者lag。获取堆栈,获取进程、CPU、内存的信息,组合后在通过plugin.onDetectIssue(issue)回调到上层。

lagTask:

@Override
public void run() {
    //1,拿到顶层activity或者fragment名字
    String scene = AppMethodBeat.getVisibleScene();
    boolean isForeground = isForeground();
    try {
        TracePlugin plugin = Matrix.with().getPluginByClass(TracePlugin.class);
        if (null == plugin) {
            return;
        }
        //2,获取主线程堆栈
        StackTraceElement[] stackTrace = Looper.getMainLooper().getThread().getStackTrace();

        JSONObject jsonObject = new JSONObject();
        //3,设备信息:设备评级、当前进程的CPU使用率,进程的总共内存、进程剩余可用内存。
        jsonObject = DeviceUtil.getDeviceInfo(jsonObject, Matrix.with().getApplication());
        // 上报类型为lag,表示延迟。
        jsonObject.put(SharePluginInfo.ISSUE_STACK_TYPE, Constants.Type.LAG);
        jsonObject.put(SharePluginInfo.ISSUE_SCENE, scene);
        //4,堆栈解析
        jsonObject.put(SharePluginInfo.ISSUE_THREAD_STACK, Utils.getStack(stackTrace));
        jsonObject.put(SharePluginInfo.ISSUE_PROCESS_FOREGROUND, isForeground);

        Issue issue = new Issue();
        //5,tag 为:evil_method:慢方法
        issue.setTag(SharePluginInfo.TAG_PLUGIN_EVIL_METHOD);
        issue.setContent(jsonObject);
        //6,往上层回调
        plugin.onDetectIssue(issue);
        MatrixLog.e(TAG, "happens lag : %s ", jsonObject.toString());

    } catch (JSONException e) {
        MatrixLog.e(TAG, "[JSONException error: %s", e);
    }

简要分析: 这里代码执行则表示message处理耗时到达了2s,收集现场信息以及主线程的调用堆栈等进行回调。

anrTask:

@Override
public void run() {

    long curTime = SystemClock.uptimeMillis();
    boolean isForeground = isForeground();
    // process 进程的优先级和nice值
    int[] processStat = Utils.getProcessPriority(Process.myPid());
    //根据开始标志beginRecord,从sBuffer获取dispatchBegin到此时的运行过的方法,得到一个数组。
    long[] data = AppMethodBeat.getInstance().copyData(beginRecord);
    beginRecord.release();
    String scene = AppMethodBeat.getVisibleScene();

    // memory 获取内存: 
    //runtime 占用内存
    //native 堆内存,通过debug获取
    //虚拟机内存,vmsize
    long[] memoryInfo = dumpMemory();

    // Thread state
    Thread.State status = Looper.getMainLooper().getThread().getState();
    StackTraceElement[] stackTrace = Looper.getMainLooper().getThread().getStackTrace();
    主线程堆栈
    String dumpStack = Utils.getStack(stackTrace, "|*\t\t", 12);

    // frame 计算choreographer的input、animation、traversal耗时
    UIThreadMonitor monitor = UIThreadMonitor.getMonitor();
    long inputCost = monitor.getQueueCost(UIThreadMonitor.CALLBACK_INPUT, token);
    long animationCost = monitor.getQueueCost(UIThreadMonitor.CALLBACK_ANIMATION, token);
    long traversalCost = monitor.getQueueCost(UIThreadMonitor.CALLBACK_TRAVERSAL, token);

    // trace 根据data数组,构造调用堆栈链
    LinkedList<MethodItem> stack = new LinkedList();
    if (data.length > 0) {
    根据data数组,构造调用堆栈链
        TraceDataUtils.structuredDataToStack(data, stack, true, curTime);
        TraceDataUtils.trimStack(stack, Constants.TARGET_EVIL_METHOD_STACK, new TraceDataUtils.IStructuredDataFilter() {
            @Override
            public boolean isFilter(long during, int filterCount) {
                return during < filterCount * Constants.TIME_UPDATE_CYCLE_MS;
            }

            @Override
            public int getFilterMaxCount() {
                return Constants.FILTER_STACK_MAX_COUNT;
            }

            @Override
            public void fallback(List<MethodItem> stack, int size) {
                MatrixLog.w(TAG, "[fallback] size:%s targetSize:%s stack:%s", size, Constants.TARGET_EVIL_METHOD_STACK, stack);
                Iterator iterator = stack.listIterator(Math.min(size, Constants.TARGET_EVIL_METHOD_STACK));
                while (iterator.hasNext()) {
                    iterator.next();
                    iterator.remove();
                }
            }
        });
    }

    StringBuilder reportBuilder = new StringBuilder();
    StringBuilder logcatBuilder = new StringBuilder();
    //整个调用栈的耗时
    long stackCost = Math.max(Constants.DEFAULT_ANR, TraceDataUtils.stackToString(stack, reportBuilder, logcatBuilder));

    // stackKey
    String stackKey = TraceDataUtils.getTreeKey(stack, stackCost);
    MatrixLog.w(TAG, "%s \npostTime:%s curTime:%s",
            printAnr(scene, processStat, memoryInfo, status, logcatBuilder, isForeground, stack.size(),
                    stackKey, dumpStack, inputCost, animationCost, traversalCost, stackCost),
            token / Constants.TIME_MILLIS_TO_NANO, curTime); // for logcat

    if (stackCost >= Constants.DEFAULT_ANR_INVALID) {
        MatrixLog.w(TAG, "The checked anr task was not executed on time. "
                + "The possible reason is that the current process has a low priority. just pass this report");
        return;
    }
    // report
    try {
        TracePlugin plugin = Matrix.with().getPluginByClass(TracePlugin.class);
        if (null == plugin) {
            return;
        }
        JSONObject jsonObject = new JSONObject();
        jsonObject = DeviceUtil.getDeviceInfo(jsonObject, Matrix.with().getApplication());
        //todo 表示发生了anr 类型
        jsonObject.put(SharePluginInfo.ISSUE_STACK_TYPE, Constants.Type.ANR);
        jsonObject.put(SharePluginInfo.ISSUE_COST, stackCost);
        jsonObject.put(SharePluginInfo.ISSUE_STACK_KEY, stackKey);
        jsonObject.put(SharePluginInfo.ISSUE_SCENE, scene);
        jsonObject.put(SharePluginInfo.ISSUE_TRACE_STACK, reportBuilder.toString());
        jsonObject.put(SharePluginInfo.ISSUE_THREAD_STACK, Utils.getStack(stackTrace));
        jsonObject.put(SharePluginInfo.ISSUE_PROCESS_PRIORITY, processStat[0]);
        jsonObject.put(SharePluginInfo.ISSUE_PROCESS_NICE, processStat[1]);
        jsonObject.put(SharePluginInfo.ISSUE_PROCESS_FOREGROUND, isForeground);
        // memory info
        JSONObject memJsonObject = new JSONObject();
        memJsonObject.put(SharePluginInfo.ISSUE_MEMORY_DALVIK, memoryInfo[0]);
        memJsonObject.put(SharePluginInfo.ISSUE_MEMORY_NATIVE, memoryInfo[1]);
        memJsonObject.put(SharePluginInfo.ISSUE_MEMORY_VM_SIZE, memoryInfo[2]);
        jsonObject.put(SharePluginInfo.ISSUE_MEMORY, memJsonObject);

        Issue issue = new Issue();
        issue.setKey(token + "");
        issue.setTag(SharePluginInfo.TAG_PLUGIN_EVIL_METHOD);
        issue.setContent(jsonObject);
        plugin.onDetectIssue(issue);

    } catch (JSONException e) {
        MatrixLog.e(TAG, "[JSONException error: %s", e);
    }

}

获取设备信息方法:


public static JSONObject getDeviceInfo(JSONObject oldObj, Application context) {
    try {
        //综合手机的RAM内存和CPU的核数,给出评级,评级越高表示设备越好
        oldObj.put(DEVICE_MACHINE, getLevel(context));
        //访问/proc/stat文件夹,计算总的CPU时间和应用本身占用的CPU时间,相除得到
        oldObj.put(DEVICE_CPU, getAppCpuRate());
        // 后面两个比较简单,通过ActivityManager 的getMemoryInfo得到
        oldObj.put(DEVICE_MEMORY, getTotalMemory(context));
        oldObj.put(DEVICE_MEMORY_FREE, getMemFree(context));

    } catch (JSONException e) {
        MatrixLog.e(TAG, "[JSONException for stack, error: %s", e);
    }

    return oldObj;
}

四、总结:

优点:对比watchDog方式,无侵入式,兼容性好。

WatchDog简要原理: 通过一个子线程往主线程加入消息,以一定的间隔去不断轮询该消息是否被处理,如果没有被处理,则表示出现了卡顿。缺点是轮询的时间间隔不好把控。间隔太小则会会主线程有影响存在性能问题,太大则容易漏报。

缺点:

  • 无法监控touch事件?

  • 无法监控idleHandlerqueueIdle()

是的,因为idelHandler的处理不会回调loggerprint方法。且每次触发消息,只会运行一次idleHandler,后续如果没有消息则会阻塞住,直到新消息的到来。

  • 无法监控SyncBarrier(同步屏障)?

当我们每次通过invalidate来刷新UI时,最终都会调用到ViewRootImpl中的scheduleTraversals方法,会向主线程的Looper中post一个SyncBarrier,其目的是为了在刷新UI时,主线程的同步消息都被跳过,此时渲染UI的异步消息就可以得到优先处理。但是我们注意到这个方法是线程不安全的,如果在非主线程中调用到了这里,就有可能会同时post多个SyncBarrier,但只能remove掉最后一个,从而有一个SyncBarrier就永远无法被remove,就导致了主线程Looper无法处理同步消息(Message默认就是同步消息),导致卡死,那如何处理?

最后,其他trace子类,准备在后面的文章中去分析~

参考:

Matrix Android TraceCanary

微信Android客户端的卡顿监控方案

Choreographer原理

你知道android的MessageQueue.IdleHandler吗?

Matrix - TraceCanary源码分析

Matrix-TraceCanary的设计和原理分析手册

Android Systrace -- 系列