本文作者:Fangx3Android是单线程模型,用户的按键事件、屏幕触摸、UI绘制都在UI线程中处理。单线程意味着串行执行。如果某个操作需要时间,则后续操作将不得不等待。这时候用户的第一感觉就是卡住了。因此,解决滞后问题的最简单方法之一就是找出耗时的方法。统计方法耗时多少?开发时统计一个方法的耗时最简单的方法就是在方法的开始和结束处分别打上时间戳,两个时间戳相减就是方法的耗时。funtake(){valstart=System.currentTimeMillis()//..service.take();//...valend=System.currentTimeMillis()valconst=end-start}上面的方法可以统计我们应用代码的耗时,但是不能统计Android系统方法的耗时。其实Android系统已经在一些关键环节上嵌入了一些点,但是它的实现并不是像我们一样嵌入时间戳,而是通过Trace类来实现的,而且Trace类也支持我们应用层调用插入的方式来自定义点,使用Android提供的systrace工具,对Trace类的点信息进行抓取和处理。最后生成一个Html文件,通过Chrome可以直观的查看一个完整链接的耗时情况。Systrace在开发阶段确实是一个强大的调优工具,但是有两个明显的局限性阻碍了这个工具的上线使用:需要连接PC,通过执行命令开启Trace功能,开发者需要手动添加Trace.beginSection和Trace.endSection,这就变成了需要开发手动添加Trace函数来预测耗时的位置,但是线上环境无法预测到哪里会耗时。所以如果能解决以上两个问题,就可以使用systrace工具进行在线排查。远离PC端运行systrace下面简单画一下systrace的工作原理:从上图可以看出,systrace抓取的数据可以分为两类:Java层发生的函数调用信息和Native层,以及内核态的事件信息。Java层Native层的函数调用信息是调用Trace类的方法收集的信息(也是我们这次需要关心的数据),数据信息会记录在trace_marker中;内核态的时间信息由LinuxFunction提供的ftrace提供,通过激活不同的事件节点,根据内核运行时的节点启用状态,将事件记录在ftrace缓冲区中。最终systrace通过检索以上两个数据整合生成一个Html文件。从上图中我们可以看出,systrace是通过Atrace来设置Tag的。如果能找到需要抓取的类型信息对应的Tag,直接在终端设置,提取trace_marker中的数据后就可以摆脱PC端的限制。.设置标签publicstaticvoidbeginSection(@NonNullStringsectionName){if(isTagEnabled(TRACE_TAG_APP)){if(sectionName.length()>MAX_SECTION_NAME_LEN){thrownewIllegalArgumentException("sectionNameistoolong");}nativeTraceBegin(TRACE_TAG_APPsectionName);}}以上是系统Trace类中的beginSecion方法。它会先判断对应的Tag是否可用,然后调用native层的TraceBegin方法写入数据。isTagEnabled的实现如下:publicstaticbooleanisTagEnabled(longtraceTag){longtags=sEnabledTags;if(tags==TRACE_TAG_NOT_READY){tags=cacheEnabledTags();}return(tags&traceTag)!=0;}dprivatestaticlongTagcaches(){longtags=nativeGetEnabledTags();sEnabledTags=标签;returntags;}看到这里,想知道是否可以通过反射修改sEnabledTags的值来开启Trace功能?通过实践可以发现,仅仅通过修改sEnabledTags是无法开启Trace功能的,所以可以大致猜测native层应该也有类似的判断。具体native代码在/system/core/libcutils/trace-dev.c(AndroidO版本代码)文件staticinlinevoidatrace_begin(uint64_ttag,constchar*name){if(CC_UNLIKELY(atrace_is_tag_enabled(tag))){voidatrace_begin_body(constchar*);atrace_begin_body(名称);可以看到这里的逻辑和Java中的处理是类似的。也是判断Tag是否可用。如果可用,则执行写入数据的逻辑。继续看atrace_is_tag_enabled的实现,staticinlineuint64_tatrace_is_tag_enabled(uint64_ttag){returnatrace_get_enabled_tags()&tag;){atrace_init();返回一个trace_enabled_tags;}可以看到获取并操作了atrace_enabled_tags字段的值,Trace类中的sEnabledTags也是通过nativeGetEnabledTags方法获取的。因此,我们应该修改下层native层的值来开启Trace功能。这里参考Facebook的profilo方案,通过dlopen获取libcuitls.so对应的句柄,从对应的symbol中找到atrace_enabled_tags的指针,设置atrace_enabled_tags开启Trace功能。std::stringlib_name("libcutils.so");std::stringenabled_tags_sym("atrace_enabled_tags");if(sdk<18){lib_name="libutils.so";enabled_tags_sym="_ZN7android6Tracer12sEnabledTagsE";}if(sdk<21){handle=dlopen(lib_name.c_str(),RTLD_LOCAL);}else{handle=dlopen(nullptr,RTLD_GLOBAL);}atrace_enabled_tags=reinterpret_cast
