版本
v0.6.5
温馨提示
TracePlugin 是比较复杂的,很多东西文章中可能讲的不是很清楚,配合 推荐 Matrix 源码完整注释
可能会有更好的效果
概述
本篇文章是 腾讯开源的 APM 框架 Matrix 系列文章的第三篇,将对 matrix-trace-canary
这个模块架构进行解析。这个模块中包含了帧率(FPS)检测,启动时间检测(APP启动和Activity启动),ANR检测,慢函数检测,这四个Tracer,后面我们会一一进行分析。上一篇为腾讯 Apm 框架 Matrix 源码阅读 - 架构解析
先看一下类图及类功能的简介。
TraceCanary架构图 .jpg- LooperMonitor:通过给主线程
Looper
设置Printer
来监控Looper
分发事件的开始和结束。 - UIThreadMonitor:反射
Choreographer
并获得每一个状态的CallbackQueue
对象 和mFrameIntervalNanos
属性值,配合LooperMonitor
使LooperObserver
具有感知每帧 开始,执行,结束具体时间的能力。 - LooperObserver:具有感知每个
Message
开始,执行,结束具体时间的能力。 - AppMethodBeat:还记得在 腾讯 Apm 框架 Matrix 源码阅读 - gradle插件中说了,在编译器Matrix会对复杂方法进行字节码插桩,那插桩的是什么内容呢?就是在复杂方法的入口插入
AppMethodBeat.i()
,方法的出口插入AppMethodBeat.O()
方法。在Activity的中插入AppMethodBeat.at()
方法。这个类主要作用就是收集各个方法的methodId和执行时间并存放在自己的sBuffer
变量中,同时AppMethodBeat
也能感知到某个activity是否获取到焦点,具体功能主要是在at
方法中。 - IAppForeground:在 腾讯 Apm 框架 Matrix 源码阅读 - 架构解析 中提到过。可以感知 APP 进入前台还是 退出到后台
- ITracer: 继承了
IAppForeground
是所有Tracer的父接口,具有isAlive()
,onStartTrace()
,onCloseTrace()
这三个抽象方法。 - Tracer:是所有Tracer的直接父类,实现了ITracer接口继承了LooperObserver类,通过上面说的Tracer天然具有 感知APP 进入前台还是 退出到后台的能力和感知每个
Message
开始,执行,结束具体时间的能力。
下面我们就从TracePlugin
这个入口类开始看。
1. TracePlugin
先看一下TracePlugin
的构造方法,没什么可以说的,就是将配置传进来并进行保存,配置中记录了那些Tracer
可用那些不可用,还记录了我们设置的SplashActivity
public TracePlugin(TraceConfig config) {
this.traceConfig = config;
}
public class TraceConfig implements IDefaultConfig {
....
public IDynamicConfig dynamicConfig;
public boolean defaultFpsEnable;
public boolean defaultMethodTraceEnable;
public boolean defaultStartupEnable;
public boolean defaultAnrEnable;
public boolean isDebug;
public boolean isDevEnv;
public String splashActivities;//可配置多个用 ; 隔开,但是生效的还是只有第一个
public Set<String> splashActivitiesSet;
....
}
1.1 TracePlugin.init()
在腾讯 Apm 框架 Matrix 源码阅读 - 架构解析中讲过每个Plugin
都具有一些生命周期方法,那么下来就依次看看TracePlugin
在这些生命周期中做了那些事情。首先是 init()
方法,可以看到init()
方法中首先判断当前SDK的版本是否小于16如果小于16就直接返回了,如果大于等于16就创建各个Tracer
。这里顺便说一下 之所以把插件的最低支持版本设置为16 是因为Choreographer.getInstance()
方法是Api16才提供的
@Override
public void init(Application app, PluginListener listener) {
//APi小于16 不支持
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;
}
anrTracer = new AnrTracer(traceConfig);
frameTracer = new FrameTracer(traceConfig);
evilMethodTracer = new EvilMethodTracer(traceConfig);
startupTracer = new StartupTracer(traceConfig);
}
1.2 TracePlugin.start()
@Override
public void start() {
super.start();
....
Runnable runnable = new Runnable() {
@Override
public void run() {
//初始化 UIThreadMonitor
if (!UIThreadMonitor.getMonitor().isInit()) {
try {
//详见【2.1】
UIThreadMonitor.getMonitor().init(traceConfig);
} catch (java.lang.RuntimeException e) {
return;
}
}
//启动 AppMethodBeat 详见【3.1】
AppMethodBeat.getInstance().onStart();
//启动 UIThreadMonitor 详见【2.9】
UIThreadMonitor.getMonitor().onStart();
anrTracer.onStartTrace();
frameTracer.onStartTrace();
evilMethodTracer.onStartTrace();
startupTracer.onStartTrace();
}
};
if (Thread.currentThread() == Looper.getMainLooper().getThread()) {
runnable.run();
} else {
//post到主线程启动
MatrixHandlerThread.getDefaultMainHandler().post(runnable);
}
}
在start()
方法中主要是在主线程中启动 UIThreadMonitor
,AppMethodBeat
还有各个Tracer
1.3 TracePlugin.stop()
@Override
public void stop() {
.....
Runnable runnable = new Runnable() {
@Override
public void run() {
//详见【3.2】
AppMethodBeat.getInstance().onStop();
UIThreadMonitor.getMonitor().onStop();
anrTracer.onCloseTrace();
frameTracer.onCloseTrace();
evilMethodTracer.onCloseTrace();
startupTracer.onCloseTrace();
}
};
if (Thread.currentThread() == Looper.getMainLooper().getThread()) {
runnable.run();
} else {
MatrixHandlerThread.getDefaultMainHandler().post(runnable);
}
}
stop()
方法中主要是在主线程中停止UIThreadMonitor
,AppMethodBeat
还有各个Tracer
1.4 TracePlugin.onForeground()
@Override
public void onForeground(boolean isForeground) {
....
if (frameTracer != null) {
frameTracer.onForeground(isForeground);
}
if (anrTracer != null) {
anrTracer.onForeground(isForeground);
}
if (evilMethodTracer != null) {
evilMethodTracer.onForeground(isForeground);
}
if (startupTracer != null) {
startupTracer.onForeground(isForeground);
}
}
onForeground
方法就是将APP处于前台或者后台的状态分发给各个Tracer
。可见TracePlugin
中的工作还是蛮简单的就是初始化,启动,分发,暂停。
2.1 UIThreadMonitor.init(traceConfig)
public void init(TraceConfig config) {
//不是主线程 就抛出异常
if (Thread.currentThread() != Looper.getMainLooper().getThread()) {
throw new AssertionError("must be init in main thread!");
}
this.config = config;
//从当前线程中获取到 Choreographer 对象
choreographer = Choreographer.getInstance();
// 获得 Choreographer 里的 mLock锁 对象
callbackQueueLock = reflectObject(choreographer, "mLock");
// 获得 Choreographer 里的 mCallbackQueues 对象
callbackQueues = reflectObject(choreographer, "mCallbackQueues");
//反射获得 callbackQueues 中第一个 CallbackQueue对象的 addCallbackLocked 的方法
// 第一个 CallbackQueue 是处理 input事件的
addInputQueue = reflectChoreographerMethod(callbackQueues[CALLBACK_INPUT], ADD_CALLBACK, long.class, Object.class, Object.class);
//反射获得 callbackQueues 中第二个 CallbackQueue对象的 addCallbackLocked 的方法
// 第二个 CallbackQueue 是处理 动画的
addAnimationQueue = reflectChoreographerMethod(callbackQueues[CALLBACK_ANIMATION], ADD_CALLBACK, long.class, Object.class, Object.class);
//反射获得 callbackQueues 中第三个 CallbackQueue对象的 addCallbackLocked 的方法
// 第三个 CallbackQueue 是绘制完 用于回调的
addTraversalQueue = reflectChoreographerMethod(callbackQueues[CALLBACK_TRAVERSAL], ADD_CALLBACK, long.class, Object.class, Object.class);
// 获取 choreographer 中mFrameIntervalNanos 的值 并赋值给 frameIntervalNanos
frameIntervalNanos = reflectObject(choreographer, "mFrameIntervalNanos");
//注册一个 LooperDispatchListener 详见【2.2】
LooperMonitor.register(new LooperMonitor.LooperDispatchListener() {
@Override
public boolean isValid() {
return isAlive;
}
@Override
public void dispatchStart() {
super.dispatchStart();
//详见【2.7】
UIThreadMonitor.this.dispatchBegin();
}
@Override
public void dispatchEnd() {
super.dispatchEnd();
//详见【2.8】
UIThreadMonitor.this.dispatchEnd();
}
});
this.isInit = true;
.....
}
UIThreadMonitor.init
主要做了三件事
- 反射获得
CALLBACK_INPUT
,CALLBACK_ANIMATION
,CALLBACK_TRAVERSAL
这三个状态的addCallbackLocked
并记录到自己的成员变量中 - 反射获得
Choreographer
记录的mFrameIntervalNanos
变量并记录到自己的成员变量中。mFrameIntervalNanos
的值在现在大部分手机设备上都是16666666 - 向
LooperMonitor
中注册一个LooperDispatchListener
监听用来监控Looper
分发事件的开始和结束。
2.2 LooperMonitor.register()
//饿汉式单例 详见【2.3】
private static final LooperMonitor mainMonitor = new LooperMonitor();
static void register(LooperDispatchListener listener) {
mainMonitor.addListener(listener);
}
LooperMonitor.register
没啥可讲,不过 LooperMonitor
对象的创建使用了饿汉式单例的方式,我们一起看看它的构造方法中干了什么
2.3 LooperMonitor <init>
public LooperMonitor(Looper looper) {
Objects.requireNonNull(looper);
this.looper = looper;
//添加 自定义的 Printer 详见【2.4】
resetPrinter();
//添加 IdleHandler
addIdleHandler(looper);
}
private LooperMonitor() {
this(Looper.getMainLooper());
}
可见无参构造获取到主线程Looper
后调用到了有参构造
2.4 LooperMonitor.resetPrinter()
private synchronized void resetPrinter() {
Printer originPrinter = null;
try {
if (!isReflectLoggingError) {
//获取之前的 Printer ,防止项目其他功能在 设置了 这个 Printer, 如果直接设置
//那其他 工具就不能正常运行了 大厂的程序员 还是细啊!!
originPrinter = ReflectUtils.get(looper.getClass(), "mLogging", looper);
//如果已经 hook过 就直接返回
if (originPrinter == printer && null != printer) {
return;
}
}
}
....
//设置自己的Printer 详见【2.5】
looper.setMessageLogging(printer = new LooperPrinter(originPrinter));
....
}
该方法主要通过反射获取主线程Looper
的Printer
进行保存,然后通过Api设置自定义的LooperPrinter
。这里保存Looper
之前的Printer
主要是防止和其他框架冲突导致其他框架不能正常工作,再感叹一次,大厂程序员还是细啊....
2.5 LooperPrinter.println()
@Override
public void println(String x) {
if (null != origin) {
//执行原始printer的 println方法
origin.println(x);
...
}
....
if (isValid) {
//详见【2.6】
dispatch(x.charAt(0) == '>', x);
}
}
2.6 LooperPrinter.dispatch()
private void dispatch(boolean isBegin, String log) {
for (LooperDispatchListener listener : listeners) {
if (listener.isValid()) {
if (isBegin) {
if (!listener.isHasDispatchStart) {
//分发开始
listener.onDispatchStart(log);
}
} else {
if (listener.isHasDispatchStart) {
//分发结束
listener.onDispatchEnd(log);
}
}
} else if (!isBegin && listener.isHasDispatchStart) {
//分发结束
listener.dispatchEnd();
}
}
}
通过该方法就会通知给所有的LooperDispatchListener
当前是Looper
刚开始分发还是已经分发完成。【2.1】中说过UIThreadMonitor.init
想注册了一个LooperDispatchListener
所以我们继续回到 UIThreadMonitor
类中
2.7 UIThreadMonitor.this.dispatchBegin
private void dispatchBegin() {
//记录 dispatch 的起始时间
token = dispatchTimeMs[0] = SystemClock.uptimeMillis();
// 记录 当前线程时间
dispatchTimeMs[2] = SystemClock.currentThreadTimeMillis();
// 调用 i 方法
AppMethodBeat.i(AppMethodBeat.METHOD_ID_DISPATCH);
synchronized (observers) {
//回调 所有 LooperObserver 的 dispatchBegin 方法
for (LooperObserver observer : observers) {
if (!observer.isDispatchBegin()) {
observer.dispatchBegin(dispatchTimeMs[0], dispatchTimeMs[2], token);
}
}
}
}
该方法中记录了dispatch
开始的时间并将 Looper开始分发事件
这个消息通知给了各个 LooperObserver
,我们在最开始介绍过Tracer
类就继承了LooperObserver
这也就是所有Tracer
都具有感知每个Message
开始,执行,结束具体时间的能力。
2.8 UIThreadMonitor.this.dispatchEnd
private void dispatchEnd() {
//帧刷新结束
if (isBelongFrame) {
//详见【2.14】
doFrameEnd(token);
}
//dispatch 起始时间
long start = token;
//dispatch 结束时间
long end = SystemClock.uptimeMillis();
synchronized (observers) {
//回调 所有 LooperObserver 的 doFrame 方法
for (LooperObserver observer : observers) {
if (observer.isDispatchBegin()) {
//参数含义 在 LooperObserver接口中查询
observer.doFrame(AppMethodBeat.getVisibleScene(), token, SystemClock.uptimeMillis(), isBelongFrame ? end - start : 0, queueCost[CALLBACK_INPUT], queueCost[CALLBACK_ANIMATION], queueCost[CALLBACK_TRAVERSAL]);
}
}
}
//记录 当前线程时间
dispatchTimeMs[3] = SystemClock.currentThreadTimeMillis();
// 记录 dispatch 的结束时间
dispatchTimeMs[1] = SystemClock.uptimeMillis();
// 调用 o 方法
AppMethodBeat.o(AppMethodBeat.METHOD_ID_DISPATCH);
synchronized (observers) {
// 回调 所有 LooperObserver的 dispatchEnd 方法
for (LooperObserver observer : observers) {
if (observer.isDispatchBegin()) {
observer.dispatchEnd(dispatchTimeMs[0], dispatchTimeMs[2], dispatchTimeMs[1], dispatchTimeMs[3], token, isBelongFrame);
}
}
}
}
这个方法中 记了了各个时间点并回调了LooperObserver
的doFrame
和dispatchEnd
方法。LooperObserver 中各个方法的各个参数的含义见附录1
2.9 UIThreadMonitor.onStart()
//标识对应 type 执行 开始 或者 结束
private int[] queueStatus = new int[CALLBACK_LAST + 1];
//type对应的 执行时间
private long[] queueCost = new long[CALLBACK_LAST + 1];
public synchronized void onStart() {
if (!isInit) {
throw new RuntimeException("never init!");
}
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];
//详见【2.10】
addFrameCallback(CALLBACK_INPUT, this, true);
}
}
这个方法主要是 重置queueStatus
和queueCost
这两个重要的成员变量,然后调用addFrameCallback
方法
2.10 UIThreadMonitor.addFrameCallback()
private synchronized void addFrameCallback(int type, Runnable callback, boolean isAddHeader) {
....
try {
synchronized (callbackQueueLock) {//和 Choreographer 中使用相同的 锁对象 都是 mLock
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) {
//反射执行 CallbackQueue 的 addCallbackLocked 方法 ,并将我们自己的 callback 添加到回到队列中,在一帧绘制完毕后
// 会回调callback的run()方法
method.invoke(callbackQueues[type], !isAddHeader ? SystemClock.uptimeMillis() : -1, callback, null);
callbackExist[type] = true;
}
}
} catch (Exception e) {
MatrixLog.e(TAG, e.toString());
}
}
如果你对Choreographer
这个类不太了解那你需要先看看Choreographer原理 。我们这里只对里面的vsync
分类简单介绍:
Choreographer
的vsync
回调事件分四类
- INPUT:输入事件(输入回调,为了尽快响应用户操作)
- ANIMATION:动画
- TRAVERSAL:窗口刷新,执行measure/layout/draw操作(处理布局和绘图的回调)
- COMMIT:遍历完成的提交操作,用来修正动画启动时间(绘制完成后的一些操作)
好了,我们继续来看UIThreadMonitor.addFrameCallback
,这个方法的功能就是将我们自定义的回调方法加入到Choreographer
的回调队列中(回调队列有多种),当一帧刷新完成后会调用我们回调的run
方法,因为【2.9】中的回调对象是this
所以我们接下来进入到UIThreadMonitor.run
2.11 UIThreadMonitor.run
@Override
public void run() {
final long start = System.nanoTime();
try {
//详见 【2.12】
doFrameBegin(token);
//详见 【2.13】
doQueueBegin(CALLBACK_INPUT);//input开始
addFrameCallback(CALLBACK_ANIMATION, new Runnable() {
@Override
public void run() {
//详见 【2.14】
doQueueEnd(CALLBACK_INPUT);//input 结束
doQueueBegin(CALLBACK_ANIMATION);//animation 开始
}
}, true);
addFrameCallback(CALLBACK_TRAVERSAL, new Runnable() {
@Override
public void run() {
doQueueEnd(CALLBACK_ANIMATION);//animation 结束
doQueueBegin(CALLBACK_TRAVERSAL);//traversal 开始
}
}, true);
} finally {
....
}
}
}
这个方法的功能就是添加CALLBACK_ANIMATION
和CALLBACK_TRAVERSAL
类型的回到到Choreographer
的回调队列中因为CALLBACK_INPUT
类型的回调已经在UIThreadMonitor.onStart
中已经添加了所以这里不需要添加。
之所以为每种Type都添加监听应该是 Matrix 设计的时候计划将每一帧的耗时细化到每种Type。否则不用这么麻烦直接调用提供的apichoreographer.postFrameCallback
就可以拿到每帧耗时
2.12 UIThreadMonitor.doFrameBegin
private void doFrameBegin(long token) {
//记录帧刷新开始
this.isBelongFrame = true;
}
2.13 UIThreadMonitor.doQueueBegin
private void doQueueBegin(int type) {
//标识当前 type 开始执行
queueStatus[type] = DO_QUEUE_BEGIN;
// 当前type 回调开始时间
queueCost[type] = System.nanoTime();
}
2.13 UIThreadMonitor.doQueueEnd
private void doQueueEnd(int type) {
//标识当前 type 执行结束
queueStatus[type] = DO_QUEUE_END;
// 当前type 执行耗时
queueCost[type] = System.nanoTime() - queueCost[type];
synchronized (this) {
// 当前type的 callback 是否还存在
callbackExist[type] = false;
}
}
2.14 UIThreadMonitor.doFrameEnd
// 记录 帧结束
private void doFrameEnd(long token) {
doQueueEnd(CALLBACK_TRAVERSAL);//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
queueStatus = new int[CALLBACK_LAST + 1];
//继续添加 input callback
addFrameCallback(CALLBACK_INPUT, this, true);
this.isBelongFrame = false;
}
在【2.8】UIThreadMonitor.this.dispatchEnd
方法中 ,也就是当一个Message
分发完毕的时候调用了doFrameEnd
该方法主要是重置 queueStatus
,并给Choreographer
的回调队列中重新添加CALLBACK_INPUT
类型的回到方法。作为下一次记录帧刷新时间的开始。
看到这里可能有人有疑惑,不是之前添加过了么,为啥这里还要再添加一次。是因为 这种监听是一次性的,Choreographer.doCallbacks()
方法最后会将所有的监听都会移除.
2.15 总结一下 UIThreadMonitor的工作原理
- 主线程Looper loop 到一个 target是
Choreographer$FrameHandler
massage 是Choreographer$FrameDisplayEventReceiver
的消息(要能触发 刷新帧) - 调用 dispatchBegin() 方法
- 调用 run方法
- 回掉 doQueueBegin type=0(input)
- 回掉 doQueueEnd type=0(input)
- 回掉 doQueueBegin type=1(animation)
- 回掉 doQueueEnd type=1(animation)
- 回掉 doQueueBegin type=2(traversal)
- Looper中message处理完毕
- 调用 dispatchEnd() 方法
- 回掉 doQueueEnd type=2(traversal)
这就是 UIThreadMonitor
获取一帧中每种Type耗时的流程。这个过程中UIThreadMonitor
会将自己的获取到的时间通过LooperObserver
这个接口分发出去。
注意:当主线程的 Looper loop 到的Message不能触发帧刷新,那么就只会执行 dispatchBegin
和dispatchEnd
这两个方法。
3.1 AppMethodBeat.onStart()
下来我们继续看AppMethodBeat
是怎么工作的
public void onStart() {
synchronized (statusLock) {
//如果没有启动 或者已经过期 则进行启动
if (status < STATUS_STARTED && status >= STATUS_EXPIRED_START) {
//取消 启动过期 检查的 Runnable
sHandler.removeCallbacks(checkStartExpiredRunnable);
if (sBuffer == null) {
throw new RuntimeException(TAG + " sBuffer == null");
}
MatrixLog.i(TAG, "[onStart] preStatus:%s", status, Utils.getStack());
//标示已将 启动
status = STATUS_STARTED;
} else {
MatrixLog.w(TAG, "[onStart] current status:%s", status);
}
}
}
onStart()
主要是将AppMethodBeat
的当前状态设置为STATUS_STARTED
。AppMethodBeat
中各个状态含义及触发时机可查看【附录.2】
3.2 AppMethodBeat.onStop()
public void onStop() {
synchronized (statusLock) {
//进行关闭
if (status == STATUS_STARTED) {
MatrixLog.i(TAG, "[onStop] %s", Utils.getStack());
status = STATUS_STOPPED;
} else {
MatrixLog.w(TAG, "[onStop] current status:%s", status);
}
}
}
onStop()
主要是将AppMethodBeat
的当前状态设置为STATUS_STOPPED
。
3.3 AppMethodBeat.i()
在最开始描述AppMethodBeat
类的时候,提到过在编译器会将AppMethodBeat
的i()
,o()
,at()
方法插桩到指定位置那么我们就来看看,这三个方法具体过了那些工作
public static void i(int methodId) {
....
//第一次执行该方法时 调用 realExecute 方法,并将状态切换为 STATUS_READY
if (status == STATUS_DEFAULT) {
synchronized (statusLock) {
if (status == STATUS_DEFAULT) {
//当 当前类 没有被启动时 执行该方法 详见【3.4】
realExecute();
//切换状态 为 STATUS_READY
status = STATUS_READY;
}
}
}
long threadId = Thread.currentThread().getId();
//执行回调
if (sMethodEnterListener != null) {
sMethodEnterListener.enter(methodId, threadId);
}
//如果是主线程
if (threadId == sMainThreadId) {
//i方法被重复执行的 提醒
if (assertIn) {
android.util.Log.e(TAG, "ERROR!!! AppMethodBeat.i Recursive calls!!!");
return;
}
assertIn = true;
if (sIndex < Constants.BUFFER_SIZE) {
//详见【3.9】
mergeData(methodId, sIndex, true);
} else {
sIndex = 0;
mergeData(methodId, sIndex, true);
}
++sIndex;
assertIn = false;
}
}
在第一次进入i()
的时候会触发调用realExecute()
方法进行一些初始化工作,然后调用mergeData()
方法保持调用时的methodId
和时间到sBuffer
中
3.4 AppMethodBeat.realExecute()
//这个方法只有 在第一次执行 i方法时才会被调用
private static void realExecute() {
MatrixLog.i(TAG, "[realExecute] timestamp:%s", System.currentTimeMillis());
//当前时间减去上一个 记录的时间 (更新 sCurrentDiffTime)
sCurrentDiffTime = SystemClock.uptimeMillis() - sDiffTime;
//清空 sHandler 所有消息
sHandler.removeCallbacksAndMessages(null);
//延迟5 ms 后执行 sUpdateDiffTimeRunnable ,开始刷新 sCurrentDiffTime 详见【3.5】
sHandler.postDelayed(sUpdateDiffTimeRunnable, Constants.TIME_UPDATE_CYCLE_MS);
//延迟15 ms 后执行 checkStartExpiredRunnable (检查 AppMethodBeat 当前状态的 runnable )
//也就是 在 realExecute 方法之后后 如果 15ms 内 AppMethodBeat 还没有被启动(onStart)
// 就将 AppMethodBeat的状态置为 STATUS_EXPIRED_START(启动过期)
// 启动过期 只是一种状态,并不会影响 AppMethodBeat 的运行
sHandler.postDelayed(checkStartExpiredRunnable = new Runnable() {
@Override
public void run() {
synchronized (statusLock) {
MatrixLog.i(TAG, "[startExpired] timestamp:%s status:%s", System.currentTimeMillis(), status);
if (status == STATUS_DEFAULT || status == STATUS_READY) {
status = STATUS_EXPIRED_START;
}
}
}
}, Constants.DEFAULT_RELEASE_BUFFER_DELAY);
//hook 主线程的 HandlerCallback 详见【3.6】
ActivityThreadHacker.hackSysHandlerCallback();
//注册 looperMonitorListener 使可以接收到looper分发massage事件 详见【3.8】
LooperMonitor.register(looperMonitorListener);
}
3.5 AppMethodBeat.sUpdateDiffTimeRunnable
private static Runnable sUpdateDiffTimeRunnable = new Runnable() {
@Override
public void run() {
try {
//无限循环 当isPauseUpdateTime=false(dispatchBegin方法完成),然后更新 sCurrentDiffTime
while (true) {
while (!isPauseUpdateTime && status > STATUS_STOPPED) {
sCurrentDiffTime = SystemClock.uptimeMillis() - sDiffTime;
SystemClock.sleep(Constants.TIME_UPDATE_CYCLE_MS);
}
synchronized (updateTimeLock) {
updateTimeLock.wait();
}
}
} catch (InterruptedException e) {
MatrixLog.e(TAG, "" + e.toString());
}
}
};
对于这个Runnable Matrix的wiki中有介绍,其中这样描述到:
考虑到每个方法执行前后都获取系统时间(System.nanoTime)会对性能影响比较大,而实际上,单个函数执行耗时小于 5ms 的情况,对卡顿来说不是主要原因,可以忽略不计,如果是多次调用的情况,则在它的父级方法中可以反映出来,所以为了减少对性能的影响,通过另一条更新时间的线程每 5ms 去更新一个时间变量,而每个方法执行前后只读取该变量来减少性能损耗。
3.6 ActivityThreadHacker.hackSysHandlerCallback()
public static void hackSysHandlerCallback() {
try {
//当前方法加载的时间 被认为是 APP启动时间,
sApplicationCreateBeginTime = SystemClock.uptimeMillis();
//记录这第一个方法,
sApplicationCreateBeginMethodIndex = AppMethodBeat.getInstance().maskIndex("ApplicationCreateBeginMethodIndex");
//替换 ActivityThread 中handler的 callBack 方法
Class<?> forName = Class.forName("android.app.ActivityThread");
Field field = forName.getDeclaredField("sCurrentActivityThread");
field.setAccessible(true);
//获得 ActivityThread 对象
Object activityThreadValue = field.get(forName);
Field mH = forName.getDeclaredField("mH");
mH.setAccessible(true);
//获得handler对象
Object handler = mH.get(activityThreadValue);
Class<?> handlerClass = handler.getClass().getSuperclass();
Field callbackField = handlerClass.getDeclaredField("mCallback");
callbackField.setAccessible(true);
//获得 Handler.Callback 对象
Handler.Callback originalCallback = (Handler.Callback) callbackField.get(handler);
//详见【3.7】
HackCallback callback = new HackCallback(originalCallback);
//设置新的callback对象
callbackField.set(handler, callback);
MatrixLog.i(TAG, "hook system handler completed. start:%s SDK_INT:%s", sApplicationCreateBeginTime, Build.VERSION.SDK_INT);
} catch (Exception e) {
MatrixLog.e(TAG, "hook system handler err! %s", e.getCause().toString());
}
}
这个方法有两个主要作用
- 记录
sApplicationCreateBeginTime
这个时间,这个时间被认为是APP启动时间。
因为在插桩的过程中忽略了 android包下的所有类,所以运行期起一个执行 AppMethodBeat.i方法的应该是 Application的attachBaseContext方法(如果有),下来就是onCreate()方法。所以这个时间被认为是APP启动时间是没有毛病的。 - hook
ActivityThread
中mH
的Handler.Callback
对象为自定义的HackCallback
对象主要用来记录最近一个Activity被打开的时间
3.7 ActivityThreadHacker.HackCallback.handleMessage()
public boolean handleMessage(Message msg) {
if (!AppMethodBeat.isRealTrace()) {
//将 handleMessage 的控制权交还给 mOriginalCallback,再一次感叹真细啊,哈哈
return null != mOriginalCallback && mOriginalCallback.handleMessage(msg);
}
//是否是打开activity的handler信息
boolean isLaunchActivity = isLaunchActivity(msg);
.....
//打开一次activity,就记录一次数据
if (isLaunchActivity) {
ActivityThreadHacker.sLastLaunchActivityTime = SystemClock.uptimeMillis();
ActivityThreadHacker.sLastLaunchActivityMethodIndex = AppMethodBeat.getInstance().maskIndex("LastLaunchActivityMethodIndex");
}
if (!isCreated) {
if (isLaunchActivity || msg.what == CREATE_SERVICE || msg.what == RECEIVER) { // 如果是启动activity、service,receiver
//发送启动Activity等消息,认为是Application 启动的结束时间
ActivityThreadHacker.sApplicationCreateEndTime = SystemClock.uptimeMillis();
ActivityThreadHacker.sApplicationCreateScene = msg.what;
isCreated = true;
}
}
//将 handleMessage 的控制权交还给 mOriginalCallback
return null != mOriginalCallback && mOriginalCallback.handleMessage(msg);
}
该方法的主要租用也有两个
- 记录最近一个Activity的启动时间
- 第一次进入时 记录 Application 的 启动结束时间
3.8 AppMethodBeat.looperMonitorListener
private static LooperMonitor.LooperDispatchListener looperMonitorListener = new LooperMonitor.LooperDispatchListener() {
@Override
public boolean isValid() {
return status >= STATUS_READY;
}
@Override
public void dispatchStart() {
super.dispatchStart();
AppMethodBeat.dispatchBegin();
}
@Override
public void dispatchEnd() {
super.dispatchEnd();
AppMethodBeat.dispatchEnd();
}
};
private static void dispatchBegin() {
//更新 sCurrentDiffTime
sCurrentDiffTime = SystemClock.uptimeMillis() - sDiffTime;
isPauseUpdateTime = false;
SystemClock.uptimeMillis(),sCurrentDiffTime);
synchronized (updateTimeLock) {
updateTimeLock.notify();
}
}
private static void dispatchEnd() {
isPauseUpdateTime = true;
}
上面我们已经了解过 LooperMonitor
可以感知到主线程Looper
开始分发和结束分发Message
的实际,AppMethodBeat
注册looperMonitorListener
监听到 LooperMonitor
主要是 为了在主线程空闲的情况下停止更新sCurrentDiffTime
在主线程开始工作时再开始更新sCurrentDiffTime
以减少资源的占用。
3.9 AppMethodBeat.mergeData()
private static void mergeData(int methodId, int index, boolean isIn) {
if (methodId == AppMethodBeat.METHOD_ID_DISPATCH) {//如果是 handler 的 dispatchMessage 方法
sCurrentDiffTime = SystemClock.uptimeMillis() - sDiffTime;
}
// 合并后的数据存到 trueId中
long trueId = 0L;
if (isIn) {//如果是 i 方法 则第63位上是1,否则为0 (就是一个标志位)
trueId |= 1L << 63;
}
//43-62位 存储 methodId
trueId |= (long) methodId << 43;
//0-42位存储 sCurrentDiffTime
trueId |= sCurrentDiffTime & 0x7FFFFFFFFFFL;
//存放到 sBuffer中
sBuffer[index] = trueId;
checkPileup(index);
sLastIndex = index;
}
这个方法的第三个参数isIn
的含义为是否是 i()
方法,i()方法中被调用为 trueo()
方法中被调用为false。
mergeData
其实就做了一件事,就是将方法类型(i或o),methodId
,sCurrentDiffTime
存到sBuffer
中的index
位置
3.10 AppMethodBeat.o()
public static void o(int methodId) {
//对 AppMethodBeat 状态进行检查
if (status <= STATUS_STOPPED) {
return;
}
//对 methodId进行校验
if (methodId >= METHOD_ID_MAX) {
return;
}
//如果是主线程
if (Thread.currentThread().getId() == sMainThreadId) {
if (sIndex < Constants.BUFFER_SIZE) {
mergeData(methodId, sIndex, false);
} else {
sIndex = 0;
mergeData(methodId, sIndex, false);
}
++sIndex;
}
}
和i()
方法一样最终也是调用了mergeData()
来存储数据到sBuffer
中
3.11 AppMethodBeat.at()
public static void at(Activity activity, boolean isFocus) {
String activityName = activity.getClass().getName();
if (isFocus) {
//获取焦点的activity 添加到 sFocusActivitySet
if (sFocusActivitySet.add(activityName)) {
synchronized (listeners) {
//广播 activityName 获取到焦点
for (IAppMethodBeatListener listener : listeners) {
listener.onActivityFocused(activityName);
}
}
// activity 不都有了吗 为啥还要通过 getVisibleScene() 获取?
MatrixLog.i(TAG, "[at] visibleScene[%s] has %s focus!", getVisibleScene(), "attach");
}
} else {
//失去焦点的activity 从sFocusActivitySet移除
if (sFocusActivitySet.remove(activityName)) {
MatrixLog.i(TAG, "[at] visibleScene[%s] has %s focus!", getVisibleScene(), "detach");
}
}
}
在刚开始我们描述AppMethodBeat
这个类的时候提到过,它能感知到某个activity是否获取到焦点。这是因为at()
方法会在编译器被插入到 Activity的onWindowFocusChanged()
方法中。
at()
方法主要的功能是自己获得的焦点信息分发给每一个IAppMethodBeatListener
3.12 AppMethodBeat.realRelease()
static {
//在 AppMethodBeat 类加载 15s 后,还没有使用(status的状态还是STATUS_DEFAULT),就清空AppMethodBeat 占用的内存
sHandler.postDelayed(new Runnable() {
@Override
public void run() {
realRelease();
}
}, Constants.DEFAULT_RELEASE_BUFFER_DELAY);
}
private static void realRelease() {
synchronized (statusLock) {
if (status == STATUS_DEFAULT) {
MatrixLog.i(TAG, "[realRelease] timestamp:%s", System.currentTimeMillis());
sHandler.removeCallbacksAndMessages(null);
//移除 looperMonitorListener 监听
LooperMonitor.unregister(looperMonitorListener);
//sTimerUpdateThread 退出
sTimerUpdateThread.quit();
sBuffer = null;
//状态改为 STATUS_OUT_RELEASE
status = STATUS_OUT_RELEASE;
}
}
}
这个方法主要是在 AppMethodBeat 类加载 15s 后,还没有使用的情况下释放各种资源,尤其是sBuffer
因为它就占用了7.6M的内存。
总结
-
UIThreadMonitor
配合LooperMonitor
获得每个刷新帧的各个阶段的耗时时间 -
AppMethodBeat
中记录了Application的启动时间和结束,Activity的启动时间和结束,每个方法的耗时时间。
系列文章
- 腾讯 Apm 框架 Matrix 源码阅读 - gradle插件
- 腾讯 Apm 框架 Matrix 源码阅读 - 架构解析
- 腾讯 Apm 框架 Matrix 源码阅读 - TracePlugin 架构解析
- 腾讯 Apm 框架 Matrix 源码阅读 - TracePlugin 之 FrameTracer
- 腾讯 Apm 框架 Matrix 源码阅读 - TracePlugin 之 StartupTracer
- 腾讯 Apm 框架 Matrix 源码阅读 - TracePlugin 之 AnrTracer
- 腾讯 Apm 框架 Matrix 源码阅读 - TracePlugin 之 上报字段含义
参考资料
附录
- LooperObserver中各个参数的含义
/**
*
* @param beginMs dispatch 的起始时间
* @param cpuBeginMs dispatch 的起始时 当前线程时间
* @param token 等于 dispatch 的起始时间
*/
@CallSuper
public void dispatchBegin(long beginMs, long cpuBeginMs, long token) {
isDispatchBegin = true;
}
/**
*
* @param focusedActivityName 当前activity的名字
* @param start looper dispatch 的起始时间
* @param end looper dispatch 的结束时间
* @param frameCostMs 该帧耗时
* @param inputCostNs input 花费 时间
* @param animationCostNs animation 花费 时间
* @param traversalCostNs traversal 花费 时间
*/
public void doFrame(String focusedActivityName, long start, long end, long frameCostMs, long inputCostNs, long animationCostNs, long traversalCostNs) {
}
/**
*
* @param beginMs dispatch 的起始时间
* @param cpuBeginMs dispatch 的起始时 当前线程时间
* @param endMs dispatch 的结束时间
* @param cpuEndMs dispatch 的结束时 当前线程时间
* @param token 等于 dispatch 的起始时间
* @param isBelongFrame 是否属于一帧? 不确定
*/
@CallSuper
public void dispatchEnd(long beginMs, long cpuBeginMs, long endMs, long cpuEndMs, long token, boolean isBelongFrame) {
isDispatchBegin = false;
}
-
AppMethodBeat
中各个状态含义及触发时机可查看
//默认状态
private static final int STATUS_DEFAULT = Integer.MAX_VALUE;
//调用onStart()后的状态
private static final int STATUS_STARTED = 2; //启动
//第一次 执行 i 方法后的状态
private static final int STATUS_READY = 1; // 准备好
//调用onStop()后的状态
private static final int STATUS_STOPPED = -1; //停止
//启动已过期 当 在 realExecute 方法之后后 如果 15ms 内 AppMethodBeat 还没有被启动(onStart)就会被置为这种状态
private static final int STATUS_EXPIRED_START = -2;
//在 AppMethodBeat 类加载 15s 后,还没有使用(status的状态还是STATUS_DEFAULT),就会被置为这种状态
private static final int STATUS_OUT_RELEASE = -3;//已释放
网友评论