BlockCanary源码解析

1.背景

随着代码复杂度越来越大,我们越来越难定位到UI卡顿的问题,所以需要用到BlockCanary来定位卡顿的原因

2.常见UI卡顿原因

2.1 出现耗时操作

2.2 布局过于复杂

2.3 过度绘制

2.4 过度measure 和 layout

2.5 内存频繁触发gc

3.使用步骤

3.1 添加依赖

    implementation 'com.github.markzhai:blockcanary-android:1.5.0'

3.2 初始化

        BlockCanary.install(this,new BlockCanaryContext()).start();

4.源码分析


 public static BlockCanary install(Context context, BlockCanaryContext blockCanaryContext) {
        BlockCanaryContext.init(context, blockCanaryContext);//初始化赋值
        setEnabled(context, DisplayActivity.class, BlockCanaryContext.get().displayNotification());//判断是否要显示通知
        return get(); //返回一个单例对象
    }


 static void init(Context context, BlockCanaryContext blockCanaryContext) {
        sApplicationContext = context;
        sInstance = blockCanaryContext;
    }


  public static BlockCanary get() {
        if (sInstance == null) {
            synchronized (BlockCanary.class) {
                if (sInstance == null) {
                    sInstance = new BlockCanary();
                }
            }
        }
      
BlockCanary 中会创建一个BlockCanaryInternals 这个方法很重要日志的打印和cpu的打印都是在这里的出现的
  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();
    }

我们再来看一下最后的start方法最后会走到println方法

    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方法中


private void startDump() {
        if (null != BlockCanaryInternals.getInstance().stackSampler) {
            BlockCanaryInternals.getInstance().stackSampler.start();
        }

        if (null != BlockCanaryInternals.getInstance().cpuSampler) {
            BlockCanaryInternals.getInstance().cpuSampler.start();
        }
    }

    private void stopDump() {
        if (null != BlockCanaryInternals.getInstance().stackSampler) {
            BlockCanaryInternals.getInstance().stackSampler.stop();
        }

        if (null != BlockCanaryInternals.getInstance().cpuSampler) {
            BlockCanaryInternals.getInstance().cpuSampler.stop();
        }
    }
我们看到 BlockCanaryInternals.getInstance().stackSampler.start();
abstract class AbstractSampler {

    private static final int DEFAULT_SAMPLE_INTERVAL = 300;

    protected AtomicBoolean mShouldSample = new AtomicBoolean(false);
    protected long mSampleInterval;

    private Runnable mRunnable = new Runnable() {
        @Override
        public void run() {
            doSample();

            if (mShouldSample.get()) {
                HandlerThreadFactory.getTimerThreadHandler()
                        .postDelayed(mRunnable, mSampleInterval);
            }
        }
    };、
其实就是两个线程 最后调用到doSample()方法我们看到doSample方法

protected void doSample() {
        BufferedReader cpuReader = null;
        BufferedReader pidReader = null;

        try {
            cpuReader = new BufferedReader(new InputStreamReader(
                    new FileInputStream("/proc/stat")), BUFFER_SIZE);
            String cpuRate = cpuReader.readLine();
            if (cpuRate == null) {
                cpuRate = "";
            }

            if (mPid == 0) {
                mPid = android.os.Process.myPid();
            }
            pidReader = new BufferedReader(new InputStreamReader(
                    new FileInputStream("/proc/" + mPid + "/stat")), BUFFER_SIZE);
            String pidCpuRate = pidReader.readLine();
            if (pidCpuRate == null) {
                pidCpuRate = "";
            }

            parse(cpuRate, pidCpuRate);
        } catch (Throwable throwable) {
            Log.e(TAG, "doSample: ", throwable);
        } finally {
            try {
                if (cpuReader != null) {
                    cpuReader.close();
                }
                if (pidReader != null) {
                    pidReader.close();
                }
            } catch (IOException exception) {
                Log.e(TAG, "doSample: ", exception);
            }
        }
    }
CPU的doSample方法就是把数据打印到文件中
StackSampler 的doSample 就是把卡顿的信息通过LinkedHashMap打印出来
    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());
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
BlockCanary是一个用于Android应用性能监控和问题排查的开库。它可以帮助开发人员监测应用的主线程中的卡顿、ANR(应用未响应)和内存泄漏等问题,并提供详细的监测数据和堆栈跟踪,以便开发人员快速定位问题。 BlockCanary的工作原理是通过在应用主线程中插入监测代码来记录主线程的耗时操作,并根据设定的阈值来判断是否发生卡顿。一旦发现卡顿,BlockCanary会收集相关的监测数据,包括耗时操作的具体信息和开始、结束时间等,并以通知的方式提醒开发人员。 BlockCanary的使用非常简单,只需要在Application的onCreate()方法中初始化BlockCanary即可。初始化时,可以设置卡顿阈值等参数,并可以选择是否将监测结果保存到文件中以供后续查看。在出现卡顿时,开发人员可以通过查看监测结果中的耗时操作和堆栈跟踪来定位问题的具体位置,并进行分析和排查。 BlockCanary的主要优点是简单易用,不需要侵入业务代码即可监测应用的性能问题。它提供了详细的监测数据和可视化的监测结果,能够帮助开发人员快速定位和解决问题。同时,BlockCanary还支持与其他性能监控工具和分析工具的集成,使其功能更加强大和灵活。 总之,BlockCanary是一个非常实用的Android应用性能监测工具,能够帮助开发人员及时发现和解决应用中的性能问题,提升用户体验。它的简单易用和丰富的功能使其成为开发人员调试和优化应用的重要利器。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值