在Android Studio Logcat中过滤关键字“Displayed”,可以看到对应的冷启动耗时日志。
使用adb shell获取应用的启动时间
adb shell am start -W [packageName]/[AppstartActivity全路径]
执行后会得到三个时间:ThisTime、TotalTime和WaitTime,详情如下:
ThisTime最后一个Activity启动耗时。
TotalTime所有Activity启动耗时。
WaitTimeAMS启动Activity的总耗时。
一般查看得到的TotalTime,即应用的启动时间,包括创建进程 + Application初始化 + Activity初始化到界面显示的过程。
面向切面编程,通过预编译和运行期动态代理实现程序功能统一维护的一种技术。
利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合性降低,提高程序的可重用性,同时大大提高了开发效率。
对哪些方法进行拦截,拦截后怎么处理。
类是对物体特征的抽象,切面就是对横切关注点的抽象。
被拦截到的点(方法、字段、构造器)。
对JoinPoint进行拦截的定义。
拦截到JoinPoint后要执行的代码,分为前置、后置、环绕三种类型。
准备 首先,为了在Android使用AOP埋点需要引入AspectJ,在项目根目录的build.gradle下加入:
classpath 'com.hujiang.aspectjx:gradle-android-plugin- aspectjx:2.0.0'
然后,在app目录下的build.gradle下加入:
apply plugin: 'android-aspectjx' implement 'org.aspectj:aspectjrt:1.8.+' 复制代码
AOP埋点实战 JoinPoint一般定位在如下位置:
首先,我们举一个小栗子:
@Before("execution(* android.app.Activity.on**(..))") public void onActivityCalled(JoinPoint joinPoint) throws Throwable { ... } 复制代码
在execution中的是一个匹配规则,第一个*代表匹配任意的方法返回值,后面的语法代码匹配所有Activity中on开头的方法。
如何统计Application中的所有方法耗时?
@Aspect public class ApplicationAop { @Around("call (* com.json.chao.application.BaseApplication.**(..))") public void getTime(ProceedingJoinPoint joinPoint) { Signature signature = joinPoint.getSignature(); String name = signature.toShortString(); long time = System.currentTimeMillis(); try { joinPoint.proceed(); } catch (Throwable throwable) { throwable.printStackTrace(); } Log.i(TAG, name + " cost" + (System.currentTimeMillis() - time)); } } 复制代码
当Action为Before、After时,方法入参为JoinPoint。 当Action为Around时,方法入参为ProceedingPoint。
ProceedingPoint不同于JoinPoint,其提供了proceed方法执行目标方法。
强烈推荐结合第三节讲解的Systrace工具使用,可以非常快速地定位到耗时方法,上线的时候,可以考虑屏蔽掉AOP功能。
//apply plugin: 'android-aspectjx'
检测开始代码处添加:
Debug.startMethodTracing();
检测结束代码处添加:
Debug.stopMethodTracing();
使用adb pull将生成的**.trace文件导出到电脑,然后使用Android Studio的Profiler加载
打开Profiler -> CPU -> 点击 Record -> 点击 Stop -> 查看Profiler下方Top Down/Bottom Up 区域找出耗时的热点方法。
会记录每个方法的时间、CPU信息。对运行时性能影响较大。
相比于Trace Java Methods会记录每个方法的时间、CPU信息,它会在应用的Java代码执行期间频繁捕获应用的调用堆栈,对运行时性能的影响比较小,能够记录更大的数据区域。
需部署到Android 8.0及以上设备,内部使用simpleperf跟踪应用的native代码,也可以命令行使用simpleperf。
检查应用与系统资源的交互情况。 查看所有核心的CPU瓶。 内部采用systrace,也可以使用systrace命令。
2、Event timeline显示应用程序中在其生命周期中转换不同状态的活动,如用户交互、屏幕旋转事件等。
3、CPU timeline显示应用程序实时CPU使用率、其它进程实时CPU使用率、应用程序使用的线程总数。
4、Thread activity timeline列出应用程序进程中的每个线程,并使用了不同的颜色在其时间轴上指示其活动。
绿色:线程处于活动状态或准备好使用CPU。 黄色:线程正等待IO操作。(重要) 灰色:线程正在睡眠,不消耗CPU时间。
提供函数跟踪数据的图形表示形式。
水平轴:表示调用的时间段和时间。 垂直轴:显示被调用方。 橙色:系统API。 绿色:应用自有方法 蓝色:第三方API(包括Java API) 提示:右键点击Jump to source跳转至指定函数。
将具有相同调用方顺序的完全相同的方法收集起来。
水平轴:执行每个方法的相对时间量。 垂直轴:显示被调用方。 注意:看顶层的哪个函数占据的宽度最大(平顶),可能存在性能问题。
递归调用列表,提供self、children、total时间和比率来表示被调用的函数信息。 Flame Chart是Top Down列表数据的图形化。
展开函数会显示其调用方。 按照消耗CPU时间由多到少的顺序对函数排序。 注意点:
Wall Clock Time:程序执行时间。 Thread Time:CPU执行的时间。
作用主要做热点分析,得到两种数据:
定义Trace静态工厂类,将Trace.begainSection(),Trace.endSection()封装成i、o方法,然后再在想要分析的方法前后进行插桩即可。
在命令行下执行systrace.py脚本:
python /Users/quchao/Library/Android/sdk/platform-tools/systrace/systrace.py -t 20 sched gfx view wm am app webview -a "com.wanandroid.json.chao" -o ~/Documents/open-project/systrace_data/wanandroid_start_1.html 复制代码
具体参数含义如下:
在UIThread一栏可以看到核心的系统方法时间区域和我们自己使用代码插桩捕获的方法时间区域。
在系统的一些关键链路(如SystemServcie、虚拟机、Binder驱动)插入一些信息(Label); 通过Label的开始和结束来确定某个核心过程的执行时间; 把这些Label信息收集起来得到系统关键路径的运行时间信息,最后得到整个系统的运行性能信息; Android Framework里面一些重要的模块都插入了label信息,用户App中可以添加自定义的Lable。
结合Android内核的数据,生成Html报告。 系统版本越高,Android Framework中添加的系统可用Label就越多,能够支持和分析的系统模块也就越多。 必须手动缩小范围,会帮助你加速收敛问题的分析过程,进而快速地定位和解决问题。
主要用于分析绘制性能方面的问题。 分析系统关键方法和应用方法耗时。
结合AOP,可以在方法的前后,非常方便地批量插入以下代码。最后从运行生成Html报告后,可以快速查找出耗时的方法。
Trace.begainSection(); Trace.endSection(); 复制代码