[源码]BlockCanary学习笔记
来源:互联网 发布:华中师范网络教育收费 编辑:程序博客网 时间:2024/05/17 09:33
BlockCanary检查APP卡顿,相关使用方法以及源码学习
一、使用方法
1.添加依赖
dependencies { // most often used way, enable notification to notify block event compile 'com.github.markzhai:blockcanary-android:1.5.0' // this way you only enable BlockCanary in debug package // debugCompile 'com.github.markzhai:blockcanary-android:1.5.0' // releaseCompile 'com.github.markzhai:blockcanary-no-op:1.5.0'}
2.在Application初始化并且启动
public class DemoApplication extends Application { @Override public void onCreate() { // ... // Do it on main process BlockCanary.install(this, new AppBlockCanaryContext()).start(); }}
二、原理
1.原理概述:给主线程Looper设置一个Printer,记录dispatchMessage方法执行的耗时时间。如果耗时超过给定的阙值,则认为主线程卡顿了。如果发生卡顿,收集所有信息(CPU、设备、堆栈)并且格式化,debug时通知烂提示,release时上传服务器。
2.Looper源码片断
for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } // This must be in a local variable, in case a UI event sets the logger 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 { msg.target.dispatchMessage(msg); } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); } msg.recycleUnchecked();}
3.BlockCanary原理图
三、源码分析
1.Application初始化以及启动
BlockCanary.install(this, new AppBlockCanaryContext()).start();
2.start过程
/** * Start monitoring. */public void start() { //防止多次启动 if (!mMonitorStarted) { mMonitorStarted = true; //给主线程设置Printer Looper.getMainLooper().setMessageLogging(mBlockCanaryCore.monitor); }}
3.intsall过程
/** * Install {@link BlockCanary} * * @param context Application context * @param blockCanaryContext BlockCanary context * @return {@link BlockCanary} */public static BlockCanary install(Context context, BlockCanaryContext blockCanaryContext) { //初始化用户配置也即是用户传入的AppBlockCanaryContext BlockCanaryContext.init(context, blockCanaryContext); //显示/隐藏桌面Block图标,也就是是否禁用组建① setEnabled(context, DisplayActivity.class, BlockCanaryContext.get().displayNotification()); //返回单例对象② return get();}
①是否启动Activity组件
private static void setEnabled(Context context, final Class<?> componentClass, final boolean enabled) { final Context appContext = context.getApplicationContext(); //单独提交到一个IO线程去执行,作者参考LeakCanary executeOnFileIoThread(new Runnable() { @Override public void run() { setEnabledBlocking(appContext, componentClass, enabled); } });}
// these lines are originally copied from LeakCanary: Copyright (C) 2015 Square, Inc.private static final Executor fileIoExecutor = newSingleThreadExecutor("File-IO");private static void setEnabledBlocking(Context appContext, Class<?> componentClass, boolean enabled) { ComponentName component = new ComponentName(appContext, componentClass); PackageManager packageManager = appContext.getPackageManager(); //根据用户配置禁止或者启动组件 int newState = enabled ? COMPONENT_ENABLED_STATE_ENABLED : COMPONENT_ENABLED_STATE_DISABLED; // Blocks on IPC. packageManager.setComponentEnabledSetting(component, newState, DONT_KILL_APP);}// end of lines copied from LeakCanary
②install返回外观类BlockCanary单例对象
/** * Get {@link BlockCanary} singleton. * 单例模式DCL * @return {@link BlockCanary} instance */public static BlockCanary get() { if (sInstance == null) { synchronized (BlockCanary.class) { if (sInstance == null) { sInstance = new BlockCanary(); } } } return sInstance;}
3.外观类BlockCanary类初始化时,会初始化核心内部类以及添加拦截器
public final class BlockCanary { private static final String TAG = "BlockCanary"; //单例对象 private static BlockCanary sInstance; //核心内部类对象 private BlockCanaryInternals mBlockCanaryCore; //是否已经开启卡顿监测,防止多次开启 private boolean mMonitorStarted = false; private BlockCanary() { //将配置赋值给内部核心类 BlockCanaryInternals.setContext(BlockCanaryContext.get()); //单例模式获取核心内部类② mBlockCanaryCore = BlockCanaryInternals.getInstance(); //添加拦截器,用户实现的BlockInterceptor接口 mBlockCanaryCore.addBlockInterceptor(BlockCanaryContext.get()); //如果用户禁用通知则不添加通知拦截器① if (!BlockCanaryContext.get().displayNotification()) { return; } mBlockCanaryCore.addBlockInterceptor(new DisplayService()); }......}
①分析内部核心类之前先看下DisplayService类,这是一个拦截器,在发生卡顿时通知栏发送通知,代码作了版本适配
final class DisplayService implements BlockInterceptor { private static final String TAG = "DisplayService"; @Override public void onBlock(Context context, BlockInfo blockInfo) { //发生卡顿时就显示通知,点击通知栏进入DisplayActivity界面 Intent intent = new Intent(context, DisplayActivity.class); intent.putExtra("show_latest", blockInfo.timeStart); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); PendingIntent pendingIntent = PendingIntent.getActivity(context, 1, intent, FLAG_UPDATE_CURRENT); String contentTitle = context.getString(R.string.block_canary_class_has_blocked, blockInfo.timeStart); String contentText = context.getString(R.string.block_canary_notification_message); show(context, contentTitle, contentText, pendingIntent); } //根据版本进行适配通知栏 @TargetApi(HONEYCOMB) private void show(Context context, String contentTitle, String contentText, PendingIntent pendingIntent) { NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); Notification notification; if (SDK_INT < HONEYCOMB) { notification = new Notification(); notification.icon = R.drawable.block_canary_notification; notification.when = System.currentTimeMillis(); notification.flags |= Notification.FLAG_AUTO_CANCEL; notification.defaults = Notification.DEFAULT_SOUND; try { Method deprecatedMethod = notification.getClass().getMethod("setLatestEventInfo", Context.class, CharSequence.class, CharSequence.class, PendingIntent.class); deprecatedMethod.invoke(notification, context, contentTitle, contentText, pendingIntent); } catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { Log.w(TAG, "Method not found", e); } } else { Notification.Builder builder = new Notification.Builder(context) .setSmallIcon(R.drawable.block_canary_notification) .setWhen(System.currentTimeMillis()) .setContentTitle(contentTitle) .setContentText(contentText) .setAutoCancel(true) .setContentIntent(pendingIntent) .setDefaults(Notification.DEFAULT_SOUND); if (SDK_INT < JELLY_BEAN) { notification = builder.getNotification(); } else { notification = builder.build(); } } notificationManager.notify(0xDEAFBEEF, notification); }}
②初始化了一个核心内部类对象,下面继续内部核心类分析跟踪过程
/** * Get BlockCanaryInternals singleton * 单例模式DCL * @return BlockCanaryInternals instance */static BlockCanaryInternals getInstance() { if (sInstance == null) { synchronized (BlockCanaryInternals.class) { if (sInstance == null) { sInstance = new BlockCanaryInternals(); } } } return sInstance;}
4.内部核心类初始化过程:初始化堆栈采样器、初始化CPU采样器、初始化Printer、删除过期日志
public BlockCanaryInternals() { //初始化堆栈采样器① stackSampler = new StackSampler( Looper.getMainLooper().getThread(), sContext.provideDumpInterval()); //初始化CPU采样器② cpuSampler = new CpuSampler(sContext.provideDumpInterval()); //设置Printer③ 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(); }
④先从软柿子捏起,先看删除日志
/** * Delete obsolete log files, which is by default 2 days. */ public static void cleanObsolete() { //写线程执行删除日志操作 HandlerThreadFactory.getWriteLogThreadHandler().post(new Runnable() { @Override public void run() { long now = System.currentTimeMillis(); //获取所有日志文件 File[] f = BlockCanaryInternals.getLogFiles(); if (f != null && f.length > 0) { synchronized (SAVE_DELETE_LOCK) { for (File aF : f) { //如果日志大于2天则删除 if (now - aF.lastModified() > OBSOLETE_DURATION) { aF.delete(); } } } } } }); }
如何获取所有日志文件
/** * 获取日志存储路径 * 根据用户设置的文件名称,如果SD卡未挂在或者不可写则使用/data目录,否则使用根目录+用户设置路径 */static String getPath() { String state = Environment.getExternalStorageState(); String logPath = BlockCanaryInternals.getContext() == null ? "" : BlockCanaryInternals.getContext().providePath(); if (Environment.MEDIA_MOUNTED.equals(state) && Environment.getExternalStorageDirectory().canWrite()) { return Environment.getExternalStorageDirectory().getPath() + logPath; } return getContext().provideContext().getFilesDir() + BlockCanaryInternals.getContext().providePath();}/** * 获取日志路径文件夹,如果不存在则创建 */static File detectedBlockDirectory() { File directory = new File(getPath()); if (!directory.exists()) { directory.mkdirs(); } return directory;}/** * 获取所有日志文件,过滤后缀.log的文件 */public static File[] getLogFiles() { File f = detectedBlockDirectory(); if (f.exists() && f.isDirectory()) { return f.listFiles(new BlockLogFileFilter()); } return null;}/** * 文件过滤器 */private static class BlockLogFileFilter implements FilenameFilter { private String TYPE = ".log"; BlockLogFileFilter() { } @Override public boolean accept(File dir, String filename) { return filename.endsWith(TYPE); }}
③采样器是对信息的收集,稍后分析,直接看设置Printer时新建出来的LooperMonitor对象
/** * 实现Printer接口必须实现println()方法 */class LooperMonitor implements Printer { //默认卡顿阙值 private static final int DEFAULT_BLOCK_THRESHOLD_MILLIS = 3000; private long mBlockThresholdMillis = DEFAULT_BLOCK_THRESHOLD_MILLIS; private long mStartTimestamp = 0; private long mStartThreadTimestamp = 0; private BlockListener mBlockListener = null; private boolean mPrintingStarted = false; //Debug时是否进行卡顿检测 private final boolean mStopWhenDebugging; public interface BlockListener { void onBlockEvent(long realStartTime, long realTimeEnd, long threadTimeStart, long threadTimeEnd); } public LooperMonitor(BlockListener blockListener, long blockThresholdMillis, boolean stopWhenDebugging) { if (blockListener == null) { throw new IllegalArgumentException("blockListener should not be null."); } mBlockListener = blockListener; mBlockThresholdMillis = blockThresholdMillis; mStopWhenDebugging = stopWhenDebugging; } @Override public void println(String x) { //Debug时用户设置是否停止检测并且是否Debug if (mStopWhenDebugging && Debug.isDebuggerConnected()) { return; } //记录开始时间,也就是dispatchMessage之前的log打印时 if (!mPrintingStarted) { mStartTimestamp = System.currentTimeMillis(); mStartThreadTimestamp = SystemClock.currentThreadTimeMillis(); mPrintingStarted = true; //启动采样器 startDump(); } else { //记录结束时间,也就是dispatchMessage之后的log打印时 final long endTime = System.currentTimeMillis(); mPrintingStarted = false; //判断是否卡顿 if (isBlock(endTime)) { //发生卡顿,回调 notifyBlockEvent(endTime); } stopDump(); } } private boolean isBlock(long endTime) { return endTime - mStartTimestamp > mBlockThresholdMillis; } private void notifyBlockEvent(final long endTime) { final long startTime = mStartTimestamp; final long startThreadTime = mStartThreadTimestamp; final long endThreadTime = SystemClock.currentThreadTimeMillis(); HandlerThreadFactory.getWriteLogThreadHandler().post(new Runnable() { @Override public void run() { mBlockListener.onBlockEvent(startTime, endTime, startThreadTime, endThreadTime); } }); } 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(); } }}
回到③,在回调方法中,格式化了信息并写入文件中,启动拦截器
//设置Printer③ 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()));
①②是采样器,主要对信息的收集,其中startDump方法会启动采样器,延迟执行,延迟时间就是卡顿的阙值乘以0.8。
/** * {@link AbstractSampler} sampler defines sampler work flow. */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); } } }; public AbstractSampler(long sampleInterval) { if (0 == sampleInterval) { sampleInterval = DEFAULT_SAMPLE_INTERVAL; } mSampleInterval = sampleInterval; } public void start() { if (mShouldSample.get()) { return; } mShouldSample.set(true); //延迟执行,延迟时间是阙值的0.8倍 HandlerThreadFactory.getTimerThreadHandler().removeCallbacks(mRunnable); HandlerThreadFactory.getTimerThreadHandler().postDelayed(mRunnable, BlockCanaryInternals.getInstance().getSampleDelay()); } public void stop() { if (!mShouldSample.get()) { return; } mShouldSample.set(false); HandlerThreadFactory.getTimerThreadHandler().removeCallbacks(mRunnable); } abstract void doSample();}
四、BlockCanary使用的设计模式有单例模式、外观模式、责任链模式。
- [源码]BlockCanary学习笔记
- BlockCanary源码分析
- BlockCanary源码分析
- 学习BlockCanary的实现
- Android-BlockCanary框架源码分析
- 关于BlockCanary的源码分析
- BlockCanary
- BlockCanary
- blockcanary UI卡顿优化框架源码解析
- BlockCanary简介
- Struts 源码学习笔记
- Struts 源码学习笔记
- NHibernate源码学习笔记
- TFTP源码 学习笔记
- Netty源码学习笔记
- FTP 源码学习笔记
- Tomcat源码学习笔记
- Android源码学习笔记
- 运营必须掌握的APP数据分析体系
- LTE-TDD measurement gaps位置计算--Python代码实现
- Spring MVC 流程图
- session和cookie
- iOS开发-AppIcon和LaunchImage
- [源码]BlockCanary学习笔记
- JAVA接口 字符串
- Idea环境下git 图形化操作
- C++ 异常处理详解
- latex符号打印总结
- 微信小程序—点击navigator中的子元素同时,防止跳转
- java并发编程实践(5)
- 一位10年Java工作经验的架构师聊Java和工作经验
- [技巧分享-软件使用] Beyond Compare添加到系统右键菜单