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 (getContext().displayNotification() && mInterceptorChain.size() != 0) {
                    for (BlockInterceptor interceptor : mInterceptorChain) {
                      interceptor.onBlock(getContext().provideContext(), blockInfo);
                    }
                  }
                }
              }
            },
            getContext().provideBlockThreshold()));

    LogWriter.cleanObsolete();
  }