…
}
}
注意到两个很关键的地方是logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what);
和logging.println![](https://www.hualigs.cn/image/61dba891ed8ee.jpg) ("<<<<这两行代码&#xff0c;它调用的时机正好在dispatchMessage(msg)
的前后&#xff0c;而主线程卡也就是在dispatchMessage(msg)
卡住了。
BlockCanary的流程图
&#xff08;图片来自网络&#xff09;
![blockcanary_flow.png](https://img.php1.cn/3cd4a/1eebe/cd5/45a090220e38e09d.webp)
BlockCanary就是通过替换系统的Printer来增加了一些我们想要的堆栈信息&#xff0c;从而满足我们的需求。
替换原有的Printer是通过以下方法&#xff1a;
Looper.getMainLooper().setMessageLogging(mainLooperPrinter);
并在mainLooperPrinter中判断start和end&#xff0c;来获取主线程dispatch该message的开始和结束时间&#xff0c;并判定该时间超过阈值(如2000毫秒)为主线程卡慢发生&#xff0c;并dump出各种信息&#xff0c;提供开发者分析性能瓶颈。如下所示&#xff1a;
&#64;Override
public void println(String x) {
if (!mStartedPrinting) {
mStartTimeMillis &#61; System.currentTimeMillis();
mStartThreadTimeMillis &#61; SystemClock.currentThreadTimeMillis();
mStartedPrinting &#61; true;
startDump();
} else {
final long endTime &#61; System.currentTimeMillis();
mStartedPrinting &#61; false;
if (isBlock(endTime)) {
notifyBlockEvent(endTime);
}
stopDump();
}
}
private boolean isBlock(long endTime) {
return endTime - mStartTimeMillis > mBlockThresholdMillis;
}
- BlockCanary dump的信息包括如下&#xff1a;
基本信息&#xff1a;安装包标示、机型、api等级、uid、CPU内核数、进程名、内存、版本号等
耗时信息&#xff1a;实际耗时、主线程时钟耗时、卡顿开始时间和结束时间
CPU信息&#xff1a;时间段内CPU是否忙&#xff0c;时间段内的系统CPU/应用CPU占比&#xff0c;I/O占CPU使用率
堆栈信息&#xff1a;发生卡慢前的最近堆栈&#xff0c;可以用来帮助定位卡慢发生的地方和重现路径
- 获取系统状态信息是通过如下代码实现&#xff1a;
threadStackSampler &#61; new ThreadStackSampler(Looper.getMainLooper().getThread(),
sBlockCanaryContext.getConfigDumpIntervalMillis());
cpuSampler &#61; new CpuSampler(sBlockCanaryContext.getConfigDumpIntervalMillis());
下面看一下ThreadStackSampler是怎么工作的&#xff1f;
protected void doSample() {
// Log.d(“BlockCanary”, “sample thread stack: [” &#43; mThreadStackEntries.size() &#43; ", " &#43; mMaxEntryCount &#43; “]”);
StringBuilder stringBuilder &#61; new StringBuilder();
// Fetch thread stack info
for (StackTraceElement stackTraceElement : mThread.getStackTrace()) {
stringBuilder.append(stackTraceElement.toString())
.append(Block.SEPARATOR);
}
// Eliminate obsolete entry
synchronized (mThreadStackEntries) {
if (mThreadStackEntries.size() &#61;&#61; mMaxEntryCount && mMaxEntryCount > 0) {
mThreadStackEntries.remove(mThreadStackEntries.keySet().iterator().next());
}
mThreadStackEntries.put(System.currentTimeMillis(), stringBuilder.toString());
}
}
直接去拿主线程的栈信息, 每半秒去拿一次, 记录下来, 如果发生卡顿就显之显示出来 拿CPU的信息较麻烦, 从/proc/stat
下面拿实时的CPU状态
, 再从/proc/" &#43; mPid &#43; "/stat
中读取进程时间, 再计算各CPU时间占比和CPU的工作状态.
基于系统WatchDog原理来实现
- 启动一个卡顿检测线程&#xff0c;该线程定期的向UI线程发送一条延迟消息&#xff0c;执行一个标志位加1的操作&#xff0c;如果规定时间内&#xff0c;标志位没有变化&#xff0c;则表示产生了卡顿。如果发生了变化&#xff0c;则代表没有长时间卡顿&#xff0c;我们重新执行延迟消息即可。
public class WatchDog {
private final static String TAG &#61; “budaye”;
//一个标志
private static final int TICK_INIT_VALUE &#61; 0;
private volatile int mTick &#61; TICK_INIT_VALUE;
//任务执行间隔
public final int DELAY_TIME &#61; 4000;
//UI线程Handler对象
private Handler mHandler &#61; new Handler(Looper.getMainLooper());
//性能监控线程
private HandlerThread mWatchDogThread &#61; new HandlerThread(“WatchDogThread”);
//性能监控线程Handler对象
private Handler mWatchDogHandler;
//定期执行的任务
private Runnable mDogRunnable &#61; new Runnable() {
&#64;Override
public void run() {
if (null &#61;&#61; mHandler) {
Log.e(TAG, “handler is null”);
return;
}
mHandler.post(new Runnable() {
&#64;Override
public void run() {//UI线程中执行
mTick&#43;&#43;;
}
});
try {
//线程休眠时间为检测任务的时间间隔
Thread.sleep(DELAY_TIME);
} catch (InterruptedException e) {
e.printStackTrace();
}
//当mTick没有自增时&#xff0c;表示产生了卡顿&#xff0c;这时打印UI线程的堆栈
if (TICK_INIT_VALUE &#61;&#61; mTick) {
StringBuilder sb &#61; new StringBuilder();
//打印堆栈信息
StackTraceElement[] stackTrace &#61; Looper.getMainLooper().getThread().getStackTrace();
for (StackTraceElement s : stackTrace) {
sb.append(s.toString() &#43; “\n”);
}
Log.d(TAG, sb.toString());
} else {
mTick &#61; TICK_INIT_VALUE;
}
mWatchDogHandler.postDelayed(mDogRunnable, DELAY_TIME);
}
};
/**
- 卡顿监控工作start方法
/
public void startWork(){
mWatchDogThread.start();
mWatchDogHandler &#61; new Handler(mWatchDogThread.getLooper());
E);
}
};
/* - 卡顿监控工作start方法
*/
public void startWork(){
mWatchDogThread.start();
mWatchDogHandler &#61; new Handler(mWatchDogThread.getLooper());