接着第一篇的分析,看看TracePlugin的start()方法。
一、TracePlugin的init()和start()
这个start方法是核心了,我们需要好好去分析。
在分析start方法之前,我们先回顾下tracePlugin的init(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的逻辑。
- 顾名思义,封装looper的anr的具体逻辑
- 封装帧率 fps相关逻辑
- 封装慢方法逻辑
- 封装启动耗时逻辑
从类的设计角度来看,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);
}
}
简要分析:
-
UIThreadMonitor类的init()初始化。这个类非常的重要,主要的工作是去通过反射方式去拿到Choreographer的队列和回调监听,从而实现自己的逻辑。loopMonitor则是监听looper中消息处理前后的逻辑,后面具体分析。 -
修改
AppMethodBeat中的状态为STATUS_STARTED,目的是isAlive()返回true,表示AppMethodBeat的i和o方法处于激活状态了。 -
调用
UIThreadMonitor的start方法 后面具体分析 -
4、5、6、7、8、9、10最终都会调用到每个tracer对应的
onAlive()方法,后面具体分析~
至此,初始化调用流程分析完毕,我们知道,tracePlugin最终的功能都体现在了每种不同类型的tracer中。因此,提出两个问题:
- 每种不同的tracer的具体是怎么实现的?实现的原理是?
- 还要理清楚tracePlugin到底是如何管理这些tracer的?类间的关系?
但是,同时需要解决另一个疑惑: UIThreadMonitor 和 AppMethodBeat是用来干什么的? 不搞清楚这个问题,无法继续分析。。。。
二、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信号到来的时候,回调callback的run方法。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 存储结构画了一张图,有助于理解:
说明:
- mCallbackQueues: 是一个数组,类型为 CallbackQueue。
- CallbackQueue:封装CallbackRecord的操作:添加、移除等
- CallbackRecord: 链表结构。存储了外部注册的callback。
有关于choreographer的详细分析,可参考Gityuan的这篇: Choreographer原理
好了,继续分析:
- 反射得到 Choreographer 的mLock锁对象,目的添加自己的callback的时候用来作为锁对象,保持跟系统锁对象的一致。
- 反射得到mCallbackQueues数组对象,用来获取具体类型的CallbackRecord中的callback队列(队列底层是通过链表实现的)
- 反射得到不同类型的addCallbackLocked方法,包含input、animation、travelsal,用来添加自定义的callback进去。
- 反射得到onVsync接收器,用来反射获取onVsync回调时候的mTimestampNanos,目的是跟系统回调同步时间。
- 反射得到mFrameIntervalNanos,帧间时长,一般等于16.66ms。用来计算掉帧率
- 通过设置Looper的mLogger对象,判断message处理开始前和结束前的回调时机,可以计算该message处理耗时。
2.1.2 LooperMonitor 类
在深入了解之前,我们先了解它的工作原理:
LooperMonitor监听的原理是利用Looper的next()会在消息处理的前后打印消息,我们只要能够监听到这两条消息之间即可计算出这条消息的耗时!
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的作用后,我们再回到之前提出两个问题:
- 每种不同的tracer的具体是怎么实现的?实现的原理是?
- 还要理清楚tracePlugin到底是如何管理这些tracer的?类间的关系?
OK,先看LooperAnrTrace类:
3.1 LooperAnrTracer 类
1,在onAlive向UIThreadMonitor注册回调,而UIThreadMonitor中有注册了LooperMonitor的回调, 最终回调到dispatchBegin和dispatchEnd方法。
@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事件?
-
无法监控
idleHandler的queueIdle()?
是的,因为idelHandler的处理不会回调logger的print方法。且每次触发消息,只会运行一次idleHandler,后续如果没有消息则会阻塞住,直到新消息的到来。
- 无法监控SyncBarrier(同步屏障)?
当我们每次通过invalidate来刷新UI时,最终都会调用到ViewRootImpl中的scheduleTraversals方法,会向主线程的Looper中post一个SyncBarrier,其目的是为了在刷新UI时,主线程的同步消息都被跳过,此时渲染UI的异步消息就可以得到优先处理。但是我们注意到这个方法是线程不安全的,如果在非主线程中调用到了这里,就有可能会同时post多个SyncBarrier,但只能remove掉最后一个,从而有一个SyncBarrier就永远无法被remove,就导致了主线程Looper无法处理同步消息(Message默认就是同步消息),导致卡死,那如何处理?
最后,其他trace子类,准备在后面的文章中去分析~
参考: