关于BlockCanary的源码分析——Grass
如下是Looper的源码,应用不断地从MessageQueue中取出Message并执行。
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
msg.recycleUnchecked();
}
在 msg.target.dispatchMessage(msg);的前后各有一次Log输出,dispatchMessage即处理一次消息,BlockCanary的原理就是计算消息处理前后的时间差,如果超出设定的阈值,则认为这是一个耗时操作。
BlockCanary.class
public void start() {
if (!mMonitorStarted) {
mMonitorStarted = true;
Looper.getMainLooper().setMessageLogging(mBlockCanaryCore.monitor);
}
}
将在BlockCanaryInternals中创建的LooperMonitor给主线程Looper的mLogging变量赋值。通过调用Looper.getMainLooper().setMessageLogging(mBlockCanaryCore.monitor);获取主线程dispatchmessage的开始和结束时间。其中,monitor变量的类型是LooperMonitor类,该类实现了Printer接口,主线程中所有的事件都会调用此方法;
@Override
public void println(String x) {
if (mStopWhenDebugging && Debug.isDebuggerConnected()) {
return;
}
if (!mPrintingStarted) {
mStartTimestamp = System.currentTimeMillis();
mStartThreadTimestamp = SystemClock.currentThreadTimeMillis();
mPrintingStarted = true;
startDump();
} else {
final long endTime = System.currentTimeMillis();
mPrintingStarted = false;
if (isBlock(endTime)) {
notifyBlockEvent(endTime);
}
stopDump();
}
}
开始会调用startDump方法开始收集信息,获取堆栈信息和CPU信息,事件结束时在 if(isBlock(endTime)) 中判断是否超过指定阻塞时间值,超过的话在notifyBlockEvent(endTime)中回调信息。而notifyBlockEvent中通过接口回调到BlockCanaryInternals类中。
public BlockCanaryInternals() {
stackSampler = new StackSampler(
Looper.getMainLooper().getThread(),
sContext.provideDumpInterval());
cpuSampler = new CpuSampler(sContext.provideDumpInterval());
setMonitor(new LooperMonitor(new LooperMonitor.BlockListener() {
@Override
public void onBlockEvent(long realTimeStart, long realTimeEnd,long threadTimeStart, long threadTimeEnd) {
// Get recent thread-stack entries and cpu usage
ArrayList<String> threadStackEntries = stackSampler.getThreadStackEntries(realTimeStart, realTimeEnd);
if (!threadStackEntries.isEmpty()) {
BlockInfo blockInfo = BlockInfo.newInstance()
.setMainThreadTimeCost(realTimeStart, realTimeEnd, threadTimeStart, threadTimeEnd)
.setCpuBusyFlag(cpuSampler.isCpuBusy(realTimeStart, realTimeEnd))
.setRecentCpuRate(cpuSampler.getCpuRateInfo())
.setThreadStackEntries(threadStackEntries)
.flushString();
LogWriter.save(blockInfo.toString());
if (mInterceptorChain.size() != 0) {
for (BlockInterceptor interceptor : mInterceptorChain) {
interceptor.onBlock(getContext().provideContext(), blockInfo);
}
}
}
}
}, getContext().provideBlockThreshold(), getContext().stopWhenDebugging()));
LogWriter.cleanObsolete();
}
BlockCanaryInternals类是事件处理事件超出设定阈值时的处理,其中的onBlockEvent方法对耗时情况进行赋值,包括方法总耗时,线程耗时,CPU是否忙,CPU占用率以及定位到具体的耗时代码行。
关于上面这段代码中的 stackSampler,是堆栈的采样,通过Main Looper获取到主线程Thread对象,调用Thread的getStackTrace方法即可获取到堆栈信息,以下是 StackSampler 中的代码:
public ArrayList<String> getThreadStackEntries(long startTime, long endTime) {
ArrayList<String> result = new ArrayList<>();
synchronized (sStackMap) {
for (Long entryTime : sStackMap.keySet()) {
if (startTime < entryTime && entryTime < endTime) {
result.add(BlockInfo.TIME_FORMATTER.format(entryTime)
+ BlockInfo.SEPARATOR
+ BlockInfo.SEPARATOR
+ sStackMap.get(entryTime));
}
}
}
return result;
}
@Override
protected void doSample() {
StringBuilder stringBuilder = new StringBuilder();
for (StackTraceElement stackTraceElement : mCurrentThread.getStackTrace()) {
stringBuilder
.append(stackTraceElement.toString())
.append(BlockInfo.SEPARATOR);
}
synchronized (sStackMap) {
if (sStackMap.size() == mMaxEntryCount && mMaxEntryCount > 0) {
sStackMap.remove(sStackMap.keySet().iterator().next());
}
sStackMap.put(System.currentTimeMillis(), stringBuilder.toString());
}
}
堆栈中耗时的代码保存在ArrayList中,堆栈则通过append拼接保存在LinkedHashMap中。