Android在效率这一块是一个需要长期优化的点,那么就需要依赖很多的第三方库以及工具,这次就从BlockCanary这个卡顿监测工具开始
引用
dependencies {
compile 'com.github.markzhai:blockcanary-android:1.5.0'
}
在应用的application中完成初始化
public class DemoApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
BlockCanary.install(this, new AppContext()).start();
}
}
//参数设置
public class AppContext extends BlockCanaryContext {
private static final String TAG = "AppContext";
@Override
public String provideQualifier() {
String qualifier = "";
try {
PackageInfo info = DemoApplication.getAppContext().getPackageManager()
.getPackageInfo(DemoApplication.getAppContext().getPackageName(), 0);
qualifier += info.versionCode + "_" + info.versionName + "_YYB";
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "provideQualifier exception", e);
}
return qualifier;
}
@Override
public int provideBlockThreshold() {
return 500;
}
@Override
public boolean displayNotification() {
return BuildConfig.DEBUG;
}
@Override
public boolean stopWhenDebugging() {
return false;
}
}
我们都知道Android应用程序只有一个主线程ActivityThread,这个主线程会创建一个Looper(Looper.prepare),而Looper又会关联一个MessageQueue,主线程Looper会在应用的生命周期内不断轮询(Looper.loop),从MessageQueue取出Message 更新UI。
那么我们需要监听所有UI的地方就在这个Lopper.loop()方法里面了,我们来看一下
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
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;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
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 slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
final long end;
try {
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (slowDispatchThresholdMs > 0) {
final long time = end - start;
if (time > slowDispatchThresholdMs) {
Slog.w(TAG, "Dispatch took " + time + "ms on "
+ Thread.currentThread().getName() + ", h=" +
msg.target + " cb=" + msg.callback + " msg=" + msg.what);
}
}
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();
}
}
我们可以看到,主要的执行主线程逻辑的代码都在logging.println两句的中间,那么我们是不是只需要重写了logging.println方法,是不是就可以监测到他这一次花了多少事件了?
那么如何来重写呢?重点就在这里了
Looper.getMainLooper().setMessageLogging(new Printer() {
@Override
public void println(String x) {
}
});
知道了这个之后,其实就只剩两件事了
1、写好计算使用了多少事件执行完毕的逻辑
2、写好获取一系列信息的放啊
1这个很好写,我们甚至不需要看BlockCanary的源码最粗暴的方式就是
Looper.getMainLooper().setMessageLogging(new Printer() {
@Override
public void println(String x) {
if(x.contains(">>>>> Dispatching")){
//这是开始
}
if(x.contains("<<<<< Finished to")){
//这是结束
}
}
});
我们来看看BlockCanary是怎么做的
从他的初始化方法开始
public static BlockCanary install(Context context, BlockCanaryContext blockCanaryContext) {
BlockCanaryContext.init(context, blockCanaryContext);
setEnabled(context, DisplayActivity.class, BlockCanaryContext.get().displayNotification());
return get();
}
private static void setEnabled(Context context,
final Class> componentClass,
final boolean enabled) {
final Context appContext = context.getApplicationContext();
executeOnFileIoThread(new Runnable() {
@Override
public void run() {
setEnabledBlocking(appContext, componentClass, enabled);
}
});
}
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);
}
public static BlockCanary get() {
if (sInstance == null) {
synchronized (BlockCanary.class) {
if (sInstance == null) {
sInstance = new BlockCanary();
}
}
}
return sInstance;
}
//私有构造函数
private BlockCanary() {
BlockCanaryInternals.setContext(BlockCanaryContext.get());
mBlockCanaryCore = BlockCanaryInternals.getInstance();
mBlockCanaryCore.addBlockInterceptor(BlockCanaryContext.get());
if (!BlockCanaryContext.get().displayNotification()) {
return;
}
mBlockCanaryCore.addBlockInterceptor(new DisplayService());
}
看看核心类BlockCanaryInternals的初始化过程
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) {
// Get recent thread-stack entries and cpu usage
ArrayList 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();
}
这里找到了我们需要的继承了Printer接口的对象LooperMonitor,以及两个采样类StackSampler和CpuSampler,即线程堆栈采样和CPU采样,通过调用setMonitor把创建的LooperMonitor赋值给BlockCanaryInternals的成员变量monitor
先来看看LooperMonitor的print方法
public void println(String x) {
if (mStopWhenDebugging && Debug.isDebuggerConnected()) {
return;
}
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();
}
}
嗯,的确是跟我们自己写的差不多的意思,
不过还没有设置到Looper对象中,那么继续看第二个方法
public void start() {
if (!mMonitorStarted) {
mMonitorStarted = true;
Looper.getMainLooper().setMessageLogging(mBlockCanaryCore.monitor);
}
}
好吧,这个方法直接就看到了设置
然后剩下的就是收集当前卡顿的点的一些细节信息了,比如当前执行的是什么方法等等,不过这一块目前就先不看了,因为主要的逻辑已经理解了,其他的细节的之后再说