在项目开发过程中,经常会碰到各种各样的卡顿,卡顿的原因有很多,跟CPU、内存、IO可能都有关,本文就目前用途广泛的BlockCancary为例子,分析它的底层实现原理
原理解析
Android Framework的很多机制都是通过消息传递机制来完成的,例如UI绘制更新,生命周期调用,ANR检测等。因此我们可以根据消息机制来追踪卡顿问题。BlockCancary也是基于主线程消息的检测流程,因此为了分析其具体实现原理,我们需要了解主线程Looper的创建
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
SamplingProfilerIntegration.start();
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
我们可以看到App启动的时候,在主线程调用Looper.prepareMainLooper() 初始化了主线程的Looper, 并且调用Looper.loop()开启了循环。
# -> Looper.java
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
...
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
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
...
msg.recycleUnchecked();
}
private Printer mLogging;
public void setMessageLogging(@Nullable Printer printer) {
mLogging = printer;
}
}
可以看到Looper从消息队列中取出消息后,在执行msg.target.dispatchMessage(msg); (这里的msg.target其实就是Handler对象)发送消息的前后,都调用了Looper的logging对象(实现了Printer接口的对象)的println方法打印了日志。从上面贴的代码可以看出Looper类中开出了一个setMessageLogging的方法,我们就可以利用这个方法来自定义日志搞事情了~。
BlockCanary源码分析
# -> BlockCanary.java
/**
* Install {@link BlockCanary}
*
* @param context Application context
* @param blockCanaryContext BlockCanary context
* @return {@link BlockCanary}
*/
public static BlockCanary install(Context context, BlockCanaryContext blockCanaryContext) {
BlockCanaryContext.init(context, blockCanaryContext);
setEnabled(context, DisplayActivity.class, BlockCanaryContext.get().displayNotification());
return get();
}
private BlockCanary() {
BlockCanaryInternals.setContext(BlockCanaryContext.get());
mBlockCanaryCore = BlockCanaryInternals.getInstance();
mBlockCanaryCore.addBlockInterceptor(BlockCanaryContext.get());
if (!BlockCanaryContext.get().displayNotification()) {
return;
}
mBlockCanaryCore.addBlockInterceptor(new DisplayService());
}
/**
* Start monitoring.
*/
public void start() {
if (!mMonitorStarted) {
mMonitorStarted = true;
Looper.getMainLooper().setMessageLogging(mBlockCanaryCore.monitor);
}
}
/**
* Stop monitoring.
*/
public void stop() {
if (mMonitorStarted) {
mMonitorStarted = false;
Looper.getMainLooper().setMessageLogging(null);
mBlockCanaryCore.stackSampler.stop();
mBlockCanaryCore.cpuSampler.stop();
}
}
# -> BlockCanaryInternals.java
public BlockCanaryInternals() {
//线程栈采集器
stackSampler = new StackSampler(
Looper.getMainLooper().getThread(),
sContext.provideDumpInterval());
//CPU采集器
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 (mInterceptorChain.size() != 0) {
for (BlockInterceptor interceptor : mInterceptorChain) {
interceptor.onBlock(getContext().provideContext(), blockInfo);
}
}
}
}
}, getContext().provideBlockThreshold(), getContext().stopWhenDebugging()));
LogWriter.cleanObsolete();
}
void addBlockInterceptor(BlockInterceptor blockInterceptor) {
mInterceptorChain.add(blockInterceptor);
}
private void setMonitor(LooperMonitor looperPrinter) {
monitor = looperPrinter;
}
在BlockCanary的构造函数中初始化了mBlockCanaryCore对象,并添加了BlockCanaryContext和DisplayService这两个实现了BlockInterceptor的类
在BlockCanary的start()中用mBlockCanaryCore.monitor这个实现了Printer接口的对象设置给了MainLopper的mLogging对象
每次调用Looper的logging.println方法时会调用到LooperMonitor 的println方法
# -> LooperMonitor.java
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;
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) {
if (!mPrintingStarted) {
mStartTimestamp = System.currentTimeMillis();
mStartThreadTimestamp = SystemClock.currentThreadTimeMillis();
mPrintingStarted = true;
startDump();
} else {
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();
}
}
}
这里判断如果mPrintingStarted为true说明前一次已经开始dump了,这里判断isBlock(long endTime) 超过了监测时间,就执行notifyBlockEvent通知观察者(BlockCanaryInternals中 指实现了BlockListener接口的LooperMonitor对象)
# -> BlockCanaryInternals.java
private List<BlockInterceptor> mInterceptorChain = new LinkedList<>();
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)
ArrayList<String> threadStackEntries = stackSampler
.getThreadStackEntries(realTimeStart, realTimeEnd);
if (mInterceptorChain.size() != 0) {
for (BlockInterceptor interceptor : mInterceptorChain) {
interceptor.onBlock(getContext().provideContext(), blockInfo);
}
}
}
}, getContext().provideBlockThreshold(), getContext().stopWhenDebugging()));
}
这里会回调mInterceptorChain的onBlock方法,其实就是指实现了BlockInterceptor接口的BlockCanaryContext和DisplayService
在start方法中分别调用stackSampler和cpuSampler做信息采集
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 void start() {
if (mShouldSample.get()) {
return;
}
mShouldSample.set(true);
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();
}
网友评论