京东前台产品研发部-资深Android工程师,主要负责手机京东Android端图片框架,性能优化,性能数据收集,对Android Framework、App性能优化有深入研究。
大型项目遇到的现状:
导致卡顿的因素有很多,常见有:
以上四条中,最主要的卡顿原因为UI线程中执行耗时操作。 我们也一直在研究,在不影响京东App性能的前提下,完美的实现一个UI线程卡顿监控系统。该系统能够监控线上用户的卡顿,上报卡顿数据,数据聚合,根据聚合结果自动生成工单,将工单发给对应模块的负责人。
希望实现的效果为:
整个系统分成4部分:
卡顿监控系统结构图:
1.主线程只有一个looper
Looper.java的源码可以看到,定义了一个静态变量sMainLooper,主线程无论有多少个Handler,但只有这一个looper存在,主线程中执行任何代码都会回到loop()函数中。
Looper.java的loop():
public static void loop() {
...
for (;;) {
...
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target);
}
msg.target.dispatchMessage(msg);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target);
}
}
...
}
就是这个mLogging,它在每个message处理的前后被调用,而如果主线程发生卡顿,是在dispatchMessage里执行了耗时操作。
2.将主线程的Printer替换
可以看到Looper中的Printer是可以替换的,谢谢Google大牛们目前留了接口,不过仔细想想,即使没有留接口也可以使用反射替换掉。
替换接口:Looper.getMainLooper().setMessageLogging(printer);
3.卡顿条件(endTime-startTime > 卡顿阈值)
Printer在每一个message执行前和执行后成对的调用,就可以知道消息开始时间startTime,以及消息的结束时间endTime。如果endTime-startTime > 卡顿阈值,则认为该条消息执行过长,主线程卡顿。
4.采样
在执行主线程的同时,采样线程要对主线程的堆栈、cpu等信息进行周期采样。执行前采样线程要先休眠一段时间,这样做的主要原因就是为了不对主线程的短消息(即执行时间很短的message)进行干扰,避免抢夺cpu资源,降低对性能的影响。
采样示意图:
1、 数据分成两类:
2、 堆栈预处理:
3、数据收集策略和展示
1、 Printer替换:由于任何一个模块都可以设置主线程的Printer,测试时发现,经常有其他未知模块会替换掉主线程的Printer,最典型的就是WebView类,WebView类中有一个setWebContentsDebuggingEnabled()函数,无论设置true还是false,都会将线程的Printer替换掉。解决办法是设置一个暗门,等h5开发同事真正需要调试时,触发暗门,调用setWebContentsDebuggingEnabled,默认不调用。
2、 getMainPrinter():Looper中并没有提供获得主线程Printer的方法。解决办法,通过反射Framework层代码获得,代码如下:
/**
* 反射获得主线程的Printer对象
* @return
*/
private static Printer getMainPrinter(){
try {
Field privatePrinterField =Looper.class.getDeclaredField("mLogging");
privatePrinterField.setAccessible(true);
Looper mainLooper = Looper.getMainLooper();
Printer oldPrinter = (Printer) privatePrinterField.get(mainLooper);//获得私有字段值
if (oldPrinter != null){
return oldPrinter;
}
} catch (Exception e) {
e.printStackTrace();
} return null;
}
卡顿收集属于我们APM监控系统的重要组成部分,收集到的卡顿数据样本越大越准确,目前手机京东App使用此系统,每天接收到用户卡顿数据上百万条,能够精准定位卡顿用户以及卡顿原因,再通过大数据的聚合,可以基本掌握每个版本的卡顿情况。
当然,卡顿数据收集只是整个工作的第一步,数据收集到服务端后,还需要QA,测试,业务研发等同学一起努力优化,才能真正的降低卡顿率。