介绍
BlockCanary 一个android UI检测工具,追踪view耗时操作;
附上地址:
https://github.com/markzhai/AndroidPerformanceMonitor
http://blog.zhaiyifan.cn/2016/01/16/BlockCanaryTransparentPerformanceMonitor/
核心代码
//BlockCanary
public void start() {
if (!mMonitorStarted) {
mMonitorStarted = true;
Looper.getMainLooper().setMessageLogging(mBlockCanaryCore.monitor);
}
}
监听loop处理事件
原来是监听主线程的 loop事件;看下怎样监听的
public static void loop() {
//...
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) {//1
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
//...
try {
msg.target.dispatchMessage(msg);//2
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
//..
if (logging != null) {//3
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
//...
}
}
原来主线程的消息队列抽取消息 去处理的前后,注释1,注释2 都会执行 logging的日志打印;而logging正是 BlockCanary传入的,实现类是
LooperMonitor;
class LooperMonitor implements Printer {
@Override
public void println(String x) {
if (mStopWhenDebugging && Debug.isDebuggerConnected()) {
return;
}
if (!mPrintingStarted) {//1
mStartTimestamp = System.currentTimeMillis();
mStartThreadTimestamp = SystemClock.currentThreadTimeMillis();
mPrintingStarted = true;
startDump();
} else {//2
final long endTime = System.currentTimeMillis();
mPrintingStarted = false;
if (isBlock(endTime)) {//3
notifyBlockEvent(endTime);
}
stopDump();
}
}
private boolean isBlock(long endTime) {
return endTime - mStartTimestamp > mBlockThresholdMillis;
}
}
注释1 记录当前时间和开始标记,代码执行到注释2的时候,自然是要记录结束时间;
执行注释3 判断handler的消息处理是否 超过阀值,如果超出 就给出ui卡顿提示 以及方法调用(stacktrace)和cpu使用信息,来帮助开发者 即使发现ui卡顿问题;
BlockCanary如何读取stacktrace和cpu使用信息
BlockCanary 有两个采样器,StackSampler和CpuSampler;
handler开始处理消息时,执行startDump;采样器开始工作
handler开始处理结束时,执行stopDumo;采样器结束工作
在handle开始处理信息时,使用Stack采样器获取方法调用信息 使用Cpu采样器获取Cpu使用信息;
在handle处理信息完毕时,采样器结束读取。
这样就获取到了handler处理消息过程中的 方法调用信息和cpu使用信息;
附StackSampler,CpuSampler 部分代码
class StackSampler extends AbstractSampler {
@Override
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());
}
}
}
class CpuSampler extends AbstractSampler {
@Override
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);
}
}
}
}
StackSampler和CpuSampler都有一个HandlerThread对象,用来在各自的子线程中 间隔性的启动任务;来读取 主线程方法调用和cpu使用信息。
网友评论