BlockCanary源码笔记

BlockCanary

核心原理离不开主线程ActivityThread,用到了Handler,Looper;在Looper循环方法,BlockCanary利用了handler原理,在 msg.target.dispatchMessage(msg);的上下方去分别打印方法执行时间,根据时间差去判断dispatchMessage是否产生了耗时的操作,是否有UI卡顿

//将打印类替换成自己的打印类,重写println就能获取消息执行前后的时间
final Printer logging = me.mLogging;
            if (logging != null) {

                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            final long traceTag = me.mTraceTag;
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            try {
//上方下方都是log输出
                msg.target.dispatchMessage(msg);
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

BlockCanary流程分析

初始化
在application中的oncreate方法中

BlockCanary.install(getApplicationContext(), new AppBlockCanaryContext()).start();
public static BlockCanary install(Context context, BlockCanaryContext blockCanaryContext) {
        //init方法仅仅做了些赋值操作
        BlockCanaryContext.init(context, blockCanaryContext);
      //根据用户消息栏通知消息配置是否开启关系来展示blockcanary界面
        setEnabled(context, DisplayActivity.class, BlockCanaryContext.get().displayNotification());
        return get();
    }

public static BlockCanary get() {
        if(sInstance == null) {
            Class var0 = BlockCanary.class;
            synchronized(BlockCanary.class) {
                if(sInstance == null) {
                    sInstance = new BlockCanary();
                }
            }
        }
        return sInstance;
    }

private BlockCanary() {
        //BlockCanary一些重要的实现放在这个内部内中实现
        BlockCanaryInternals.setContext(BlockCanaryContext.get());
//BlockCanaryInternals的单例
        this.mBlockCanaryCore = BlockCanaryInternals.getInstance();
        this.mBlockCanaryCore.addBlockInterceptor(BlockCanaryContext.get());
//通知栏是否开启,release是不显示的
        if(BlockCanaryContext.get().displayNotification()) {
//展示页面
            this.mBlockCanaryCore.addBlockInterceptor(new DisplayService());
        }
    }

由上面的代码分析出核心的一些功能在BlockCanary的内部类BlockCanaryInternals中实现

public final class BlockCanaryInternals {
    LooperMonitor monitor;
    StackSampler stackSampler;
    CpuSampler cpuSampler;
    private static BlockCanaryInternals sInstance;
    private static BlockCanaryContext sContext;
    private List mInterceptorChain = new LinkedList();

    public BlockCanaryInternals() {
//dump出我们线程的stack信息,传入主线程以及dump的时间间隔
        this.stackSampler = new StackSampler(Looper.getMainLooper().getThread(), (long)sContext.provideDumpInterval());
//dump出CPU的信息
        this.cpuSampler = new CpuSampler((long)sContext.provideDumpInterval());
//LooperMonitor很重要,因为通过LooperMonitor来打印dispatchMessage时上
//下的时间
        this.setMonitor(new LooperMonitor(new BlockListener() {
          //当消息处理时间大于阈值,回调打印主线程调用栈,cpu的使用情况,内存情况等
            public void onBlockEvent(long realTimeStart, long realTimeEnd, long threadTimeStart, long threadTimeEnd) {
                ArrayList threadStackEntries = BlockCanaryInternals.this.stackSampler.getThreadStackEntries(realTimeStart, realTimeEnd);
                if(!threadStackEntries.isEmpty()) {
                    BlockInfo blockInfo = BlockInfo.newInstance().setMainThreadTimeCost(realTimeStart, realTimeEnd, threadTimeStart, threadTimeEnd).setCpuBusyFlag(BlockCanaryInternals.this.cpuSampler.isCpuBusy(realTimeStart, realTimeEnd)).setRecentCpuRate(BlockCanaryInternals.this.cpuSampler.getCpuRateInfo()).setThreadStackEntries(threadStackEntries).flushString();
                    LogWriter.save(blockInfo.toString());
                    if(BlockCanaryInternals.this.mInterceptorChain.size() != 0) {
                        Iterator var11 = BlockCanaryInternals.this.mInterceptorChain.iterator();

                        while(var11.hasNext()) {
                            BlockInterceptor interceptor = (BlockInterceptor)var11.next();
                            interceptor.onBlock(BlockCanaryInternals.sContext.provideContext(), blockInfo);
                        }
                    }
                }

            }
        }, (long)getContext().provideBlockThreshold(), getContext().stopWhenDebugging()));
//删除日志
        LogWriter.cleanObsolete();
    }
}
stacksampler/cpusampler/start方法
上面分析了install方法,现在分析下start方法
BlockCanary.install(getApplicationContext(), new AppBlockCanaryContext()).start();

public void start() {
        if(!this.mMonitorStarted) {
            this.mMonitorStarted = true;
//monitor为LooperMonitor
            Looper.getMainLooper().setMessageLogging(this.mBlockCanaryCore.monitor);
        }

//Looper类中
public void setMessageLogging(@Nullable Printer printer) {
        mLogging = printer;
    }

    }

LooperMonitor类中有个重要的方法println,之后Looper中的日志打印将走这个方法。

class LooperMonitor implements Printer {
    public void println(String x) {
        if(!this.mStopWhenDebugging || !Debug.isDebuggerConnected()) {
            if(!this.mPrintingStarted) {
                this.mStartTimestamp = System.currentTimeMillis();
//当前线程运行的总时间,sleep跟wait不会记录这个时间当中
                this.mStartThreadTimestamp = SystemClock.currentThreadTimeMillis();
                this.mPrintingStarted = true;
//开始dump
                this.startDump();
            } else {
                long endTime = System.currentTimeMillis();
                this.mPrintingStarted = false;
//如果message执行后减去执行前的时间大于阈值
                if(this.isBlock(endTime)) {
                    //执行回调
                    this.notifyBlockEvent(endTime);
                }
    //停止dump
                this.stopDump();
            }
        }
    }

private void startDump() {
        if(null != BlockCanaryInternals.getInstance().stackSampler) {
//开启HandlerThread线程处理doSample()方法,doSample()主要是把当前时间
//(key)跟当前线程栈信息(value)存入linkedHashmap中
            BlockCanaryInternals.getInstance().stackSampler.start();
        }

        if(null != BlockCanaryInternals.getInstance().cpuSampler) {
//同stackSampler,开启HandlerThread线程处理doSample()方法,主要是读
//取/proc/stat文件跟"/proc/" + this.mPid + "/stat"文件中的信息
            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();
        }

    }

}

StackSampler类:

private static final LinkedHashMap sStackMap = new LinkedHashMap();

protected void doSample() {
        StringBuilder stringBuilder = new StringBuilder();
//获取当前线程的调用栈,mCurrentThread这里代表主线程
        StackTraceElement[] var2 = this.mCurrentThread.getStackTrace();
        int var3 = var2.length;

        for(int var4 = 0; var4 < var3; ++var4) {
            StackTraceElement stackTraceElement = var2[var4];
            stringBuilder.append(stackTraceElement.toString()).append("\r\n");
        }

        LinkedHashMap var8 = sStackMap;
        synchronized(sStackMap) {
            if(sStackMap.size() == this.mMaxEntryCount && this.mMaxEntryCount > 0) {
                sStackMap.remove(sStackMap.keySet().iterator().next());
            }
//sStackMap为LinkedHashMap,能记录我们先后插入顺序
            sStackMap.put(Long.valueOf(System.currentTimeMillis()), stringBuilder.toString());
        }
    }

相关问题

一.ANR问题:Application Not Responding

在android中由Activity Manager和WindowManager来监控,当出现以下三种情况会报ANR
1.Service Timeout:服务在20s没执行
2.BroadcastQueue Timeout:前台广播在10s没执行
3.inputDispatching Timeout:输入事件在5s没执行(触屏,按键等)

主要原因:主线程在规定时间内没完成任务。具体分以下几类
1.主线程在做一些耗时工作
2.主线程被其他线程锁
3.cpu被其他进程占用

如何解决:
1.主线程读取数据,耗时的数据库读取
2.sharePreference的commit(同步),apply(异步)
3.不要在广播的onReceive中做耗时操作
4.Activity的生命周期不应该有太多耗时操作

watchdog-anr 如何检测ANR
使用:在Application中开启new ANRWatchDog().start();
原理:ANRWatchDog继承Thread
1.创建监测线程
2.该线程不断往UI线程post一个任务
3.睡眠固定时间
4.等该线程重新起来后检测之前post的任务是否执行了(任务执行的是++操作,判断只需要判断是否执行了++)

public class ANRWatchDog extends Thread {
      private final Handler _uiHandler = new Handler(Looper.getMainLooper());
      @Override
    public void run() {
        setName("|ANR-WatchDog|");

        int lastTick;
        int lastIgnored = -1;
        while (!isInterrupted()) {
            lastTick = _tick;
            _uiHandler.post(_ticker);
            try {
                Thread.sleep(_timeoutInterval);
            }
            catch (InterruptedException e) {
                _interruptionListener.onInterrupted(e);
                return ;
            }

            // If the main thread has not handled _ticker, it is blocked. ANR.
            if (_tick == lastTick) {
                if (!_ignoreDebugger && Debug.isDebuggerConnected()) {
                    if (_tick != lastIgnored)
                        Log.w("ANRWatchdog", "An ANR was detected but ignored because the debugger is connected (you can prevent this with setIgnoreDebugger(true))");
                    lastIgnored = _tick;
                    continue ;
                }

                ANRError error;
                if (_namePrefix != null)
                    error = ANRError.New(_namePrefix, _logThreadsWithoutStackTrace);
                else
                    error = ANRError.NewMainOnly();
                _anrListener.onAppNotResponding(error);
                return;
            }
        }
    }
}
二.线程,多线程问题

1.new Tread问题
调用start启动线程(就绪状态),实现了多线程;run方法表示一个普通的方法,不代表多线程(??)
1).多个耗时任务就会开启多个新线程,开销非常大
2).如果在线程中执行循环任务,只能通过一个Flag来控制它的停止
3).没有线程切换的接口(只能通过handler或者其他的线程通信方式)
4).如果从UI线程启动,则该线程优先级默认为Default(和UI线程相同,可能还是会阻塞主线程);Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

2.线程通信
多线程编程时有两大原则
1).不要阻塞UI线程
2).不要在UI线程之外访问UI组件

线程通信分两类
1).将任务从工作线程抛到主线程
2).将任务从主线程抛到工作线程

工作线程抛任务到主线程(不管是哪种方式,最终都是通过Handler)
1).通过Handler
2).activity.runOnUiThread(runnalbe);
3).AsynkTask

任务从主线程到工作线程
1).Thread/Runnable
2).HandlerThread
3).IntentService

你可能感兴趣的:(BlockCanary源码笔记)