关于BlockCanary的源码分析

来源:互联网 发布:超级优化基因液txt网盘 编辑:程序博客网 时间:2024/05/19 06:38

关于BlockCanary的源码分析——Grass

如下是Looper的源码,应用不断地从MessageQueue中取出Message并执行。

for (;;) {   Message msg = queue.next(); // might blockif (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.classpublic void start() {    if (!mMonitorStarted) {        mMonitorStarted = true;        Looper.getMainLooper().setMessageLogging(mBlockCanaryCore.monitor);    }}

将在BlockCanaryInternals中创建的LooperMonitor给主线程Looper的mLogging变量赋值。通过调用Looper.getMainLooper().setMessageLogging(mBlockCanaryCore.monitor);获取主线程dispatchmessage的开始和结束时间。其中,monitor变量的类型是LooperMonitor类,该类实现了Printer接口,主线程中所有的事件都会调用此方法;

@Overridepublic 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;}@Overrideprotected 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中。

原创粉丝点击