版本
v0.6.5
溫馨提示
TracePlugin 是比較復(fù)雜的,很多東西文章中可能講的不是很清楚,配合 推薦 Matrix 源碼完整注釋
可能會(huì)有更好的效果
概述
本篇文章是 騰訊開源的 APM 框架 Matrix 系列文章的第三篇,將對(duì) matrix-trace-canary這個(gè)模塊架構(gòu)進(jìn)行解析。這個(gè)模塊中包含了幀率(FPS)檢測(cè),啟動(dòng)時(shí)間檢測(cè)(APP啟動(dòng)和Activity啟動(dòng)),ANR檢測(cè),慢函數(shù)檢測(cè),這四個(gè)Tracer,后面我們會(huì)一一進(jìn)行分析。上一篇為騰訊 Apm 框架 Matrix 源碼閱讀 - 架構(gòu)解析
先看一下類圖及類功能的簡(jiǎn)介。

- LooperMonitor:通過給主線程
Looper設(shè)置Printer來監(jiān)控Looper分發(fā)事件的開始和結(jié)束。 - UIThreadMonitor:反射
Choreographer并獲得每一個(gè)狀態(tài)的CallbackQueue對(duì)象 和mFrameIntervalNanos屬性值,配合LooperMonitor使LooperObserver具有感知每幀 開始,執(zhí)行,結(jié)束具體時(shí)間的能力。 - LooperObserver:具有感知每個(gè)
Message開始,執(zhí)行,結(jié)束具體時(shí)間的能力。 - AppMethodBeat:還記得在 騰訊 Apm 框架 Matrix 源碼閱讀 - gradle插件中說了,在編譯器Matrix會(huì)對(duì)復(fù)雜方法進(jìn)行字節(jié)碼插樁,那插樁的是什么內(nèi)容呢?就是在復(fù)雜方法的入口插入
AppMethodBeat.i(),方法的出口插入AppMethodBeat.O()方法。在Activity的中插入AppMethodBeat.at()方法。這個(gè)類主要作用就是收集各個(gè)方法的methodId和執(zhí)行時(shí)間并存放在自己的sBuffer變量中,同時(shí)AppMethodBeat也能感知到某個(gè)activity是否獲取到焦點(diǎn),具體功能主要是在at方法中。 - IAppForeground:在 騰訊 Apm 框架 Matrix 源碼閱讀 - 架構(gòu)解析 中提到過??梢愿兄?APP 進(jìn)入前臺(tái)還是 退出到后臺(tái)
- ITracer: 繼承了
IAppForeground是所有Tracer的父接口,具有isAlive(),onStartTrace(),onCloseTrace()這三個(gè)抽象方法。 - Tracer:是所有Tracer的直接父類,實(shí)現(xiàn)了ITracer接口繼承了LooperObserver類,通過上面說的Tracer天然具有 感知APP 進(jìn)入前臺(tái)還是 退出到后臺(tái)的能力和感知每個(gè)
Message開始,執(zhí)行,結(jié)束具體時(shí)間的能力。
下面我們就從TracePlugin這個(gè)入口類開始看。
1. TracePlugin
先看一下TracePlugin的構(gòu)造方法,沒什么可以說的,就是將配置傳進(jìn)來并進(jìn)行保存,配置中記錄了那些Tracer可用那些不可用,還記錄了我們?cè)O(shè)置的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;//可配置多個(gè)用 ; 隔開,但是生效的還是只有第一個(gè)
public Set<String> splashActivitiesSet;
....
}
1.1 TracePlugin.init()
在騰訊 Apm 框架 Matrix 源碼閱讀 - 架構(gòu)解析中講過每個(gè)Plugin都具有一些生命周期方法,那么下來就依次看看TracePlugin在這些生命周期中做了那些事情。首先是 init()方法,可以看到init()方法中首先判斷當(dāng)前SDK的版本是否小于16如果小于16就直接返回了,如果大于等于16就創(chuàng)建各個(gè)Tracer。這里順便說一下 之所以把插件的最低支持版本設(shè)置為16 是因?yàn)?code>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;
}
}
//啟動(dòng) AppMethodBeat 詳見【3.1】
AppMethodBeat.getInstance().onStart();
//啟動(dòng) UIThreadMonitor 詳見【2.9】
UIThreadMonitor.getMonitor().onStart();
anrTracer.onStartTrace();
frameTracer.onStartTrace();
evilMethodTracer.onStartTrace();
startupTracer.onStartTrace();
}
};
if (Thread.currentThread() == Looper.getMainLooper().getThread()) {
runnable.run();
} else {
//post到主線程啟動(dòng)
MatrixHandlerThread.getDefaultMainHandler().post(runnable);
}
}
在start()方法中主要是在主線程中啟動(dòng) UIThreadMonitor,AppMethodBeat還有各個(gè)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還有各個(gè)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處于前臺(tái)或者后臺(tái)的狀態(tài)分發(fā)給各個(gè)Tracer。可見TracePlugin中的工作還是蠻簡(jiǎn)單的就是初始化,啟動(dòng),分發(fā),暫停。
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;
//從當(dāng)前線程中獲取到 Choreographer 對(duì)象
choreographer = Choreographer.getInstance();
// 獲得 Choreographer 里的 mLock鎖 對(duì)象
callbackQueueLock = reflectObject(choreographer, "mLock");
// 獲得 Choreographer 里的 mCallbackQueues 對(duì)象
callbackQueues = reflectObject(choreographer, "mCallbackQueues");
//反射獲得 callbackQueues 中第一個(gè) CallbackQueue對(duì)象的 addCallbackLocked 的方法
// 第一個(gè) CallbackQueue 是處理 input事件的
addInputQueue = reflectChoreographerMethod(callbackQueues[CALLBACK_INPUT], ADD_CALLBACK, long.class, Object.class, Object.class);
//反射獲得 callbackQueues 中第二個(gè) CallbackQueue對(duì)象的 addCallbackLocked 的方法
// 第二個(gè) CallbackQueue 是處理 動(dòng)畫的
addAnimationQueue = reflectChoreographerMethod(callbackQueues[CALLBACK_ANIMATION], ADD_CALLBACK, long.class, Object.class, Object.class);
//反射獲得 callbackQueues 中第三個(gè) CallbackQueue對(duì)象的 addCallbackLocked 的方法
// 第三個(gè) CallbackQueue 是繪制完 用于回調(diào)的
addTraversalQueue = reflectChoreographerMethod(callbackQueues[CALLBACK_TRAVERSAL], ADD_CALLBACK, long.class, Object.class, Object.class);
// 獲取 choreographer 中mFrameIntervalNanos 的值 并賦值給 frameIntervalNanos
frameIntervalNanos = reflectObject(choreographer, "mFrameIntervalNanos");
//注冊(cè)一個(gè) 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這三個(gè)狀態(tài)的addCallbackLocked并記錄到自己的成員變量中 - 反射獲得
Choreographer記錄的mFrameIntervalNanos變量并記錄到自己的成員變量中。mFrameIntervalNanos的值在現(xiàn)在大部分手機(jī)設(shè)備上都是16666666 - 向
LooperMonitor中注冊(cè)一個(gè)LooperDispatchListener監(jiān)聽用來監(jiān)控Looper分發(fā)事件的開始和結(jié)束。
2.2 LooperMonitor.register()
//餓漢式單例 詳見【2.3】
private static final LooperMonitor mainMonitor = new LooperMonitor();
static void register(LooperDispatchListener listener) {
mainMonitor.addListener(listener);
}
LooperMonitor.register沒啥可講,不過 LooperMonitor對(duì)象的創(chuàng)建使用了餓漢式單例的方式,我們一起看看它的構(gòu)造方法中干了什么
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());
}
可見無參構(gòu)造獲取到主線程Looper后調(diào)用到了有參構(gòu)造
2.4 LooperMonitor.resetPrinter()
private synchronized void resetPrinter() {
Printer originPrinter = null;
try {
if (!isReflectLoggingError) {
//獲取之前的 Printer ,防止項(xiàng)目其他功能在 設(shè)置了 這個(gè) Printer, 如果直接設(shè)置
//那其他 工具就不能正常運(yùn)行了 大廠的程序員 還是細(xì)?。?!
originPrinter = ReflectUtils.get(looper.getClass(), "mLogging", looper);
//如果已經(jīng) hook過 就直接返回
if (originPrinter == printer && null != printer) {
return;
}
}
}
....
//設(shè)置自己的Printer 詳見【2.5】
looper.setMessageLogging(printer = new LooperPrinter(originPrinter));
....
}
該方法主要通過反射獲取主線程Looper的Printer進(jìn)行保存,然后通過Api設(shè)置自定義的LooperPrinter。這里保存Looper之前的Printer主要是防止和其他框架沖突導(dǎo)致其他框架不能正常工作,再感嘆一次,大廠程序員還是細(xì)啊....
2.5 LooperPrinter.println()
@Override
public void println(String x) {
if (null != origin) {
//執(zhí)行原始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) {
//分發(fā)開始
listener.onDispatchStart(log);
}
} else {
if (listener.isHasDispatchStart) {
//分發(fā)結(jié)束
listener.onDispatchEnd(log);
}
}
} else if (!isBegin && listener.isHasDispatchStart) {
//分發(fā)結(jié)束
listener.dispatchEnd();
}
}
}
通過該方法就會(huì)通知給所有的LooperDispatchListener當(dāng)前是Looper剛開始分發(fā)還是已經(jīng)分發(fā)完成?!?.1】中說過UIThreadMonitor.init想注冊(cè)了一個(gè)LooperDispatchListener所以我們繼續(xù)回到 UIThreadMonitor類中
2.7 UIThreadMonitor.this.dispatchBegin
private void dispatchBegin() {
//記錄 dispatch 的起始時(shí)間
token = dispatchTimeMs[0] = SystemClock.uptimeMillis();
// 記錄 當(dāng)前線程時(shí)間
dispatchTimeMs[2] = SystemClock.currentThreadTimeMillis();
// 調(diào)用 i 方法
AppMethodBeat.i(AppMethodBeat.METHOD_ID_DISPATCH);
synchronized (observers) {
//回調(diào) 所有 LooperObserver 的 dispatchBegin 方法
for (LooperObserver observer : observers) {
if (!observer.isDispatchBegin()) {
observer.dispatchBegin(dispatchTimeMs[0], dispatchTimeMs[2], token);
}
}
}
}
該方法中記錄了dispatch開始的時(shí)間并將 Looper開始分發(fā)事件 這個(gè)消息通知給了各個(gè) LooperObserver,我們?cè)谧铋_始介紹過Tracer類就繼承了LooperObserver這也就是所有Tracer都具有感知每個(gè)Message開始,執(zhí)行,結(jié)束具體時(shí)間的能力。
2.8 UIThreadMonitor.this.dispatchEnd
private void dispatchEnd() {
//幀刷新結(jié)束
if (isBelongFrame) {
//詳見【2.14】
doFrameEnd(token);
}
//dispatch 起始時(shí)間
long start = token;
//dispatch 結(jié)束時(shí)間
long end = SystemClock.uptimeMillis();
synchronized (observers) {
//回調(diào) 所有 LooperObserver 的 doFrame 方法
for (LooperObserver observer : observers) {
if (observer.isDispatchBegin()) {
//參數(shù)含義 在 LooperObserver接口中查詢
observer.doFrame(AppMethodBeat.getVisibleScene(), token, SystemClock.uptimeMillis(), isBelongFrame ? end - start : 0, queueCost[CALLBACK_INPUT], queueCost[CALLBACK_ANIMATION], queueCost[CALLBACK_TRAVERSAL]);
}
}
}
//記錄 當(dāng)前線程時(shí)間
dispatchTimeMs[3] = SystemClock.currentThreadTimeMillis();
// 記錄 dispatch 的結(jié)束時(shí)間
dispatchTimeMs[1] = SystemClock.uptimeMillis();
// 調(diào)用 o 方法
AppMethodBeat.o(AppMethodBeat.METHOD_ID_DISPATCH);
synchronized (observers) {
// 回調(diào) 所有 LooperObserver的 dispatchEnd 方法
for (LooperObserver observer : observers) {
if (observer.isDispatchBegin()) {
observer.dispatchEnd(dispatchTimeMs[0], dispatchTimeMs[2], dispatchTimeMs[1], dispatchTimeMs[3], token, isBelongFrame);
}
}
}
}
這個(gè)方法中 記了了各個(gè)時(shí)間點(diǎn)并回調(diào)了LooperObserver的doFrame和dispatchEnd方法。LooperObserver 中各個(gè)方法的各個(gè)參數(shù)的含義見附錄1
2.9 UIThreadMonitor.onStart()
//標(biāo)識(shí)對(duì)應(yīng) type 執(zhí)行 開始 或者 結(jié)束
private int[] queueStatus = new int[CALLBACK_LAST + 1];
//type對(duì)應(yīng)的 執(zhí)行時(shí)間
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);
}
}
這個(gè)方法主要是 重置queueStatus和queueCost這兩個(gè)重要的成員變量,然后調(diào)用addFrameCallback方法
2.10 UIThreadMonitor.addFrameCallback()
private synchronized void addFrameCallback(int type, Runnable callback, boolean isAddHeader) {
....
try {
synchronized (callbackQueueLock) {//和 Choreographer 中使用相同的 鎖對(duì)象 都是 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) {
//反射執(zhí)行 CallbackQueue 的 addCallbackLocked 方法 ,并將我們自己的 callback 添加到回到隊(duì)列中,在一幀繪制完畢后
// 會(huì)回調(diào)callback的run()方法
method.invoke(callbackQueues[type], !isAddHeader ? SystemClock.uptimeMillis() : -1, callback, null);
callbackExist[type] = true;
}
}
} catch (Exception e) {
MatrixLog.e(TAG, e.toString());
}
}
如果你對(duì)Choreographer這個(gè)類不太了解那你需要先看看Choreographer原理 。我們這里只對(duì)里面的vsync分類簡(jiǎn)單介紹:
Choreographer的vsync回調(diào)事件分四類
- INPUT:輸入事件(輸入回調(diào),為了盡快響應(yīng)用戶操作)
- ANIMATION:動(dòng)畫
- TRAVERSAL:窗口刷新,執(zhí)行measure/layout/draw操作(處理布局和繪圖的回調(diào))
- COMMIT:遍歷完成的提交操作,用來修正動(dòng)畫啟動(dòng)時(shí)間(繪制完成后的一些操作)
好了,我們繼續(xù)來看UIThreadMonitor.addFrameCallback,這個(gè)方法的功能就是將我們自定義的回調(diào)方法加入到Choreographer的回調(diào)隊(duì)列中(回調(diào)隊(duì)列有多種),當(dāng)一幀刷新完成后會(huì)調(diào)用我們回調(diào)的run方法,因?yàn)椤?.9】中的回調(diào)對(duì)象是this所以我們接下來進(jìn)入到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 結(jié)束
doQueueBegin(CALLBACK_ANIMATION);//animation 開始
}
}, true);
addFrameCallback(CALLBACK_TRAVERSAL, new Runnable() {
@Override
public void run() {
doQueueEnd(CALLBACK_ANIMATION);//animation 結(jié)束
doQueueBegin(CALLBACK_TRAVERSAL);//traversal 開始
}
}, true);
} finally {
....
}
}
}
這個(gè)方法的功能就是添加CALLBACK_ANIMATION和CALLBACK_TRAVERSAL類型的回到到Choreographer的回調(diào)隊(duì)列中因?yàn)?code>CALLBACK_INPUT類型的回調(diào)已經(jīng)在UIThreadMonitor.onStart中已經(jīng)添加了所以這里不需要添加。
之所以為每種Type都添加監(jiān)聽?wèi)?yīng)該是 Matrix 設(shè)計(jì)的時(shí)候計(jì)劃將每一幀的耗時(shí)細(xì)化到每種Type。否則不用這么麻煩直接調(diào)用提供的apichoreographer.postFrameCallback就可以拿到每幀耗時(shí)
2.12 UIThreadMonitor.doFrameBegin
private void doFrameBegin(long token) {
//記錄幀刷新開始
this.isBelongFrame = true;
}
2.13 UIThreadMonitor.doQueueBegin
private void doQueueBegin(int type) {
//標(biāo)識(shí)當(dāng)前 type 開始執(zhí)行
queueStatus[type] = DO_QUEUE_BEGIN;
// 當(dāng)前type 回調(diào)開始時(shí)間
queueCost[type] = System.nanoTime();
}
2.13 UIThreadMonitor.doQueueEnd
private void doQueueEnd(int type) {
//標(biāo)識(shí)當(dāng)前 type 執(zhí)行結(jié)束
queueStatus[type] = DO_QUEUE_END;
// 當(dāng)前type 執(zhí)行耗時(shí)
queueCost[type] = System.nanoTime() - queueCost[type];
synchronized (this) {
// 當(dāng)前type的 callback 是否還存在
callbackExist[type] = false;
}
}
2.14 UIThreadMonitor.doFrameEnd
// 記錄 幀結(jié)束
private void doFrameEnd(long token) {
doQueueEnd(CALLBACK_TRAVERSAL);//traversal 結(jié)束
// 如果有 沒有結(jié)束的回調(diào) 則報(bào)錯(cuò)
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];
//繼續(xù)添加 input callback
addFrameCallback(CALLBACK_INPUT, this, true);
this.isBelongFrame = false;
}
在【2.8】UIThreadMonitor.this.dispatchEnd方法中 ,也就是當(dāng)一個(gè)Message分發(fā)完畢的時(shí)候調(diào)用了doFrameEnd
該方法主要是重置 queueStatus,并給Choreographer的回調(diào)隊(duì)列中重新添加CALLBACK_INPUT類型的回到方法。作為下一次記錄幀刷新時(shí)間的開始。
看到這里可能有人有疑惑,不是之前添加過了么,為啥這里還要再添加一次。是因?yàn)?這種監(jiān)聽是一次性的,Choreographer.doCallbacks()方法最后會(huì)將所有的監(jiān)聽都會(huì)移除.
2.15 總結(jié)一下 UIThreadMonitor的工作原理
- 主線程Looper loop 到一個(gè) target是
Choreographer$FrameHandlermassage 是Choreographer$FrameDisplayEventReceiver的消息(要能觸發(fā) 刷新幀) - 調(diào)用 dispatchBegin() 方法
- 調(diào)用 run方法
- 回掉 doQueueBegin type=0(input)
- 回掉 doQueueEnd type=0(input)
- 回掉 doQueueBegin type=1(animation)
- 回掉 doQueueEnd type=1(animation)
- 回掉 doQueueBegin type=2(traversal)
- Looper中message處理完畢
- 調(diào)用 dispatchEnd() 方法
- 回掉 doQueueEnd type=2(traversal)
這就是 UIThreadMonitor獲取一幀中每種Type耗時(shí)的流程。這個(gè)過程中UIThreadMonitor會(huì)將自己的獲取到的時(shí)間通過LooperObserver這個(gè)接口分發(fā)出去。
注意:當(dāng)主線程的 Looper loop 到的Message不能觸發(fā)幀刷新,那么就只會(huì)執(zhí)行 dispatchBegin和dispatchEnd這兩個(gè)方法。
3.1 AppMethodBeat.onStart()
下來我們繼續(xù)看AppMethodBeat是怎么工作的
public void onStart() {
synchronized (statusLock) {
//如果沒有啟動(dòng) 或者已經(jīng)過期 則進(jìn)行啟動(dòng)
if (status < STATUS_STARTED && status >= STATUS_EXPIRED_START) {
//取消 啟動(dòng)過期 檢查的 Runnable
sHandler.removeCallbacks(checkStartExpiredRunnable);
if (sBuffer == null) {
throw new RuntimeException(TAG + " sBuffer == null");
}
MatrixLog.i(TAG, "[onStart] preStatus:%s", status, Utils.getStack());
//標(biāo)示已將 啟動(dòng)
status = STATUS_STARTED;
} else {
MatrixLog.w(TAG, "[onStart] current status:%s", status);
}
}
}
onStart()主要是將AppMethodBeat 的當(dāng)前狀態(tài)設(shè)置為STATUS_STARTED。AppMethodBeat 中各個(gè)狀態(tài)含義及觸發(fā)時(shí)機(jī)可查看【附錄.2】
3.2 AppMethodBeat.onStop()
public void onStop() {
synchronized (statusLock) {
//進(jìn)行關(guān)閉
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 的當(dāng)前狀態(tài)設(shè)置為STATUS_STOPPED。
3.3 AppMethodBeat.i()
在最開始描述AppMethodBeat類的時(shí)候,提到過在編譯器會(huì)將AppMethodBeat的i(),o(),at()方法插樁到指定位置那么我們就來看看,這三個(gè)方法具體過了那些工作
public static void i(int methodId) {
....
//第一次執(zhí)行該方法時(shí) 調(diào)用 realExecute 方法,并將狀態(tài)切換為 STATUS_READY
if (status == STATUS_DEFAULT) {
synchronized (statusLock) {
if (status == STATUS_DEFAULT) {
//當(dāng) 當(dāng)前類 沒有被啟動(dòng)時(shí) 執(zhí)行該方法 詳見【3.4】
realExecute();
//切換狀態(tài) 為 STATUS_READY
status = STATUS_READY;
}
}
}
long threadId = Thread.currentThread().getId();
//執(zhí)行回調(diào)
if (sMethodEnterListener != null) {
sMethodEnterListener.enter(methodId, threadId);
}
//如果是主線程
if (threadId == sMainThreadId) {
//i方法被重復(fù)執(zhí)行的 提醒
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;
}
}
在第一次進(jìn)入i()的時(shí)候會(huì)觸發(fā)調(diào)用realExecute()方法進(jìn)行一些初始化工作,然后調(diào)用mergeData()方法保持調(diào)用時(shí)的methodId和時(shí)間到sBuffer中
3.4 AppMethodBeat.realExecute()
//這個(gè)方法只有 在第一次執(zhí)行 i方法時(shí)才會(huì)被調(diào)用
private static void realExecute() {
MatrixLog.i(TAG, "[realExecute] timestamp:%s", System.currentTimeMillis());
//當(dāng)前時(shí)間減去上一個(gè) 記錄的時(shí)間 (更新 sCurrentDiffTime)
sCurrentDiffTime = SystemClock.uptimeMillis() - sDiffTime;
//清空 sHandler 所有消息
sHandler.removeCallbacksAndMessages(null);
//延遲5 ms 后執(zhí)行 sUpdateDiffTimeRunnable ,開始刷新 sCurrentDiffTime 詳見【3.5】
sHandler.postDelayed(sUpdateDiffTimeRunnable, Constants.TIME_UPDATE_CYCLE_MS);
//延遲15 ms 后執(zhí)行 checkStartExpiredRunnable (檢查 AppMethodBeat 當(dāng)前狀態(tài)的 runnable )
//也就是 在 realExecute 方法之后后 如果 15ms 內(nèi) AppMethodBeat 還沒有被啟動(dòng)(onStart)
// 就將 AppMethodBeat的狀態(tài)置為 STATUS_EXPIRED_START(啟動(dòng)過期)
// 啟動(dòng)過期 只是一種狀態(tài),并不會(huì)影響 AppMethodBeat 的運(yùn)行
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();
//注冊(cè) looperMonitorListener 使可以接收到looper分發(fā)massage事件 詳見【3.8】
LooperMonitor.register(looperMonitorListener);
}
3.5 AppMethodBeat.sUpdateDiffTimeRunnable
private static Runnable sUpdateDiffTimeRunnable = new Runnable() {
@Override
public void run() {
try {
//無限循環(huán) 當(dāng)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());
}
}
};
對(duì)于這個(gè)Runnable Matrix的wiki中有介紹,其中這樣描述到:
考慮到每個(gè)方法執(zhí)行前后都獲取系統(tǒng)時(shí)間(System.nanoTime)會(huì)對(duì)性能影響比較大,而實(shí)際上,單個(gè)函數(shù)執(zhí)行耗時(shí)小于 5ms 的情況,對(duì)卡頓來說不是主要原因,可以忽略不計(jì),如果是多次調(diào)用的情況,則在它的父級(jí)方法中可以反映出來,所以為了減少對(duì)性能的影響,通過另一條更新時(shí)間的線程每 5ms 去更新一個(gè)時(shí)間變量,而每個(gè)方法執(zhí)行前后只讀取該變量來減少性能損耗。
3.6 ActivityThreadHacker.hackSysHandlerCallback()
public static void hackSysHandlerCallback() {
try {
//當(dāng)前方法加載的時(shí)間 被認(rèn)為是 APP啟動(dòng)時(shí)間,
sApplicationCreateBeginTime = SystemClock.uptimeMillis();
//記錄這第一個(gè)方法,
sApplicationCreateBeginMethodIndex = AppMethodBeat.getInstance().maskIndex("ApplicationCreateBeginMethodIndex");
//替換 ActivityThread 中handler的 callBack 方法
Class<?> forName = Class.forName("android.app.ActivityThread");
Field field = forName.getDeclaredField("sCurrentActivityThread");
field.setAccessible(true);
//獲得 ActivityThread 對(duì)象
Object activityThreadValue = field.get(forName);
Field mH = forName.getDeclaredField("mH");
mH.setAccessible(true);
//獲得handler對(duì)象
Object handler = mH.get(activityThreadValue);
Class<?> handlerClass = handler.getClass().getSuperclass();
Field callbackField = handlerClass.getDeclaredField("mCallback");
callbackField.setAccessible(true);
//獲得 Handler.Callback 對(duì)象
Handler.Callback originalCallback = (Handler.Callback) callbackField.get(handler);
//詳見【3.7】
HackCallback callback = new HackCallback(originalCallback);
//設(shè)置新的callback對(duì)象
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());
}
}
這個(gè)方法有兩個(gè)主要作用
- 記錄
sApplicationCreateBeginTime這個(gè)時(shí)間,這個(gè)時(shí)間被認(rèn)為是APP啟動(dòng)時(shí)間。
因?yàn)樵诓鍢兜倪^程中忽略了 android包下的所有類,所以運(yùn)行期起一個(gè)執(zhí)行 AppMethodBeat.i方法的應(yīng)該是 Application的attachBaseContext方法(如果有),下來就是onCreate()方法。所以這個(gè)時(shí)間被認(rèn)為是APP啟動(dòng)時(shí)間是沒有毛病的。 - hook
ActivityThread中mH的Handler.Callback對(duì)象為自定義的HackCallback對(duì)象主要用來記錄最近一個(gè)Activity被打開的時(shí)間
3.7 ActivityThreadHacker.HackCallback.handleMessage()
public boolean handleMessage(Message msg) {
if (!AppMethodBeat.isRealTrace()) {
//將 handleMessage 的控制權(quán)交還給 mOriginalCallback,再一次感嘆真細(xì)啊,哈哈
return null != mOriginalCallback && mOriginalCallback.handleMessage(msg);
}
//是否是打開activity的handler信息
boolean isLaunchActivity = isLaunchActivity(msg);
.....
//打開一次activity,就記錄一次數(shù)據(jù)
if (isLaunchActivity) {
ActivityThreadHacker.sLastLaunchActivityTime = SystemClock.uptimeMillis();
ActivityThreadHacker.sLastLaunchActivityMethodIndex = AppMethodBeat.getInstance().maskIndex("LastLaunchActivityMethodIndex");
}
if (!isCreated) {
if (isLaunchActivity || msg.what == CREATE_SERVICE || msg.what == RECEIVER) { // 如果是啟動(dòng)activity、service,receiver
//發(fā)送啟動(dòng)Activity等消息,認(rèn)為是Application 啟動(dòng)的結(jié)束時(shí)間
ActivityThreadHacker.sApplicationCreateEndTime = SystemClock.uptimeMillis();
ActivityThreadHacker.sApplicationCreateScene = msg.what;
isCreated = true;
}
}
//將 handleMessage 的控制權(quán)交還給 mOriginalCallback
return null != mOriginalCallback && mOriginalCallback.handleMessage(msg);
}
該方法的主要租用也有兩個(gè)
- 記錄最近一個(gè)Activity的啟動(dòng)時(shí)間
- 第一次進(jìn)入時(shí) 記錄 Application 的 啟動(dòng)結(jié)束時(shí)間
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;
}
上面我們已經(jīng)了解過 LooperMonitor可以感知到主線程Looper開始分發(fā)和結(jié)束分發(fā)Message的實(shí)際,AppMethodBeat注冊(cè)looperMonitorListener監(jiān)聽到 LooperMonitor主要是 為了在主線程空閑的情況下停止更新sCurrentDiffTime在主線程開始工作時(shí)再開始更新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;
}
// 合并后的數(shù)據(jù)存到 trueId中
long trueId = 0L;
if (isIn) {//如果是 i 方法 則第63位上是1,否則為0 (就是一個(gè)標(biāo)志位)
trueId |= 1L << 63;
}
//43-62位 存儲(chǔ) methodId
trueId |= (long) methodId << 43;
//0-42位存儲(chǔ) sCurrentDiffTime
trueId |= sCurrentDiffTime & 0x7FFFFFFFFFFL;
//存放到 sBuffer中
sBuffer[index] = trueId;
checkPileup(index);
sLastIndex = index;
}
這個(gè)方法的第三個(gè)參數(shù)isIn的含義為是否是 i()方法,i()方法中被調(diào)用為 trueo()方法中被調(diào)用為false。
mergeData其實(shí)就做了一件事,就是將方法類型(i或o),methodId,sCurrentDiffTime存到sBuffer中的index位置
3.10 AppMethodBeat.o()
public static void o(int methodId) {
//對(duì) AppMethodBeat 狀態(tài)進(jìn)行檢查
if (status <= STATUS_STOPPED) {
return;
}
//對(duì) methodId進(jìn)行校驗(yàn)
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()方法一樣最終也是調(diào)用了mergeData()來存儲(chǔ)數(shù)據(jù)到sBuffer中
3.11 AppMethodBeat.at()
public static void at(Activity activity, boolean isFocus) {
String activityName = activity.getClass().getName();
if (isFocus) {
//獲取焦點(diǎn)的activity 添加到 sFocusActivitySet
if (sFocusActivitySet.add(activityName)) {
synchronized (listeners) {
//廣播 activityName 獲取到焦點(diǎn)
for (IAppMethodBeatListener listener : listeners) {
listener.onActivityFocused(activityName);
}
}
// activity 不都有了嗎 為啥還要通過 getVisibleScene() 獲???
MatrixLog.i(TAG, "[at] visibleScene[%s] has %s focus!", getVisibleScene(), "attach");
}
} else {
//失去焦點(diǎn)的activity 從sFocusActivitySet移除
if (sFocusActivitySet.remove(activityName)) {
MatrixLog.i(TAG, "[at] visibleScene[%s] has %s focus!", getVisibleScene(), "detach");
}
}
}
在剛開始我們描述AppMethodBeat這個(gè)類的時(shí)候提到過,它能感知到某個(gè)activity是否獲取到焦點(diǎn)。這是因?yàn)?code>at()方法會(huì)在編譯器被插入到 Activity的onWindowFocusChanged()方法中。
at()方法主要的功能是自己獲得的焦點(diǎn)信息分發(fā)給每一個(gè)IAppMethodBeatListener
3.12 AppMethodBeat.realRelease()
static {
//在 AppMethodBeat 類加載 15s 后,還沒有使用(status的狀態(tài)還是STATUS_DEFAULT),就清空AppMethodBeat 占用的內(nèi)存
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 監(jiān)聽
LooperMonitor.unregister(looperMonitorListener);
//sTimerUpdateThread 退出
sTimerUpdateThread.quit();
sBuffer = null;
//狀態(tài)改為 STATUS_OUT_RELEASE
status = STATUS_OUT_RELEASE;
}
}
}
這個(gè)方法主要是在 AppMethodBeat 類加載 15s 后,還沒有使用的情況下釋放各種資源,尤其是sBuffer因?yàn)樗驼加昧?.6M的內(nèi)存。
總結(jié)
-
UIThreadMonitor配合LooperMonitor獲得每個(gè)刷新幀的各個(gè)階段的耗時(shí)時(shí)間 -
AppMethodBeat中記錄了Application的啟動(dòng)時(shí)間和結(jié)束,Activity的啟動(dòng)時(shí)間和結(jié)束,每個(gè)方法的耗時(shí)時(shí)間。
系列文章
- 騰訊 Apm 框架 Matrix 源碼閱讀 - gradle插件
- 騰訊 Apm 框架 Matrix 源碼閱讀 - 架構(gòu)解析
- 騰訊 Apm 框架 Matrix 源碼閱讀 - TracePlugin 架構(gòu)解析
- 騰訊 Apm 框架 Matrix 源碼閱讀 - TracePlugin 之 FrameTracer
- 騰訊 Apm 框架 Matrix 源碼閱讀 - TracePlugin 之 StartupTracer
- 騰訊 Apm 框架 Matrix 源碼閱讀 - TracePlugin 之 AnrTracer
- 騰訊 Apm 框架 Matrix 源碼閱讀 - TracePlugin 之 上報(bào)字段含義
參考資料
附錄
- LooperObserver中各個(gè)參數(shù)的含義
/**
*
* @param beginMs dispatch 的起始時(shí)間
* @param cpuBeginMs dispatch 的起始時(shí) 當(dāng)前線程時(shí)間
* @param token 等于 dispatch 的起始時(shí)間
*/
@CallSuper
public void dispatchBegin(long beginMs, long cpuBeginMs, long token) {
isDispatchBegin = true;
}
/**
*
* @param focusedActivityName 當(dāng)前activity的名字
* @param start looper dispatch 的起始時(shí)間
* @param end looper dispatch 的結(jié)束時(shí)間
* @param frameCostMs 該幀耗時(shí)
* @param inputCostNs input 花費(fèi) 時(shí)間
* @param animationCostNs animation 花費(fèi) 時(shí)間
* @param traversalCostNs traversal 花費(fèi) 時(shí)間
*/
public void doFrame(String focusedActivityName, long start, long end, long frameCostMs, long inputCostNs, long animationCostNs, long traversalCostNs) {
}
/**
*
* @param beginMs dispatch 的起始時(shí)間
* @param cpuBeginMs dispatch 的起始時(shí) 當(dāng)前線程時(shí)間
* @param endMs dispatch 的結(jié)束時(shí)間
* @param cpuEndMs dispatch 的結(jié)束時(shí) 當(dāng)前線程時(shí)間
* @param token 等于 dispatch 的起始時(shí)間
* @param isBelongFrame 是否屬于一幀? 不確定
*/
@CallSuper
public void dispatchEnd(long beginMs, long cpuBeginMs, long endMs, long cpuEndMs, long token, boolean isBelongFrame) {
isDispatchBegin = false;
}
-
AppMethodBeat中各個(gè)狀態(tài)含義及觸發(fā)時(shí)機(jī)可查看
//默認(rèn)狀態(tài)
private static final int STATUS_DEFAULT = Integer.MAX_VALUE;
//調(diào)用onStart()后的狀態(tài)
private static final int STATUS_STARTED = 2; //啟動(dòng)
//第一次 執(zhí)行 i 方法后的狀態(tài)
private static final int STATUS_READY = 1; // 準(zhǔn)備好
//調(diào)用onStop()后的狀態(tài)
private static final int STATUS_STOPPED = -1; //停止
//啟動(dòng)已過期 當(dāng) 在 realExecute 方法之后后 如果 15ms 內(nèi) AppMethodBeat 還沒有被啟動(dòng)(onStart)就會(huì)被置為這種狀態(tài)
private static final int STATUS_EXPIRED_START = -2;
//在 AppMethodBeat 類加載 15s 后,還沒有使用(status的狀態(tài)還是STATUS_DEFAULT),就會(huì)被置為這種狀態(tài)
private static final int STATUS_OUT_RELEASE = -3;//已釋放