关于BlockCanary的源码分析

关于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中。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值