1.adb logcat 查看冷启动时间和Activity显示时间:
过滤Displayed关键字,可看到Activity的显示时间
那上面display后面的是时间是指包含哪些过程的时间呢?
模拟在Application中沉睡1秒操作,冷启动情况下:
从上可知:在冷启动情况下,第一个activity的display是冷启动的总时间=application+Activity的时间。
接着,界面上点击按钮,随意启动一个Activity:
从上可知:app进程在线情况下,启动activity的display 时间才是activity的显示时间(onCreate()->onStart()->onResume())
2.adb shell 启动activity的时间:
格式:
adb shell am start -W package名/包名路径的activity
例如,执行打开指定的mainActivity,如下图所示:
解读以上数据
- TotalTime: 是Activity的启动时间 ,与adb logcat 中Display 时间规则一致(通常以这个时间为准)
- WaitTime: 是AMS启动Activity的总耗时(它会TotalTime更大,存在跨进程通讯回执时间)
通过adb shell 启动的activity 必须具备以下条件之一:
设置程序的主入口:
或者设置exported为true
3.AndroidStudio的cpu Profile:
3.1适用于app冷启动时的trace捕捉:
创建一个新的运行configurations, 在profiling中配置:
一般是选择 java method sample,即Profiler的sample 模式,对性能影响小。系统会间隔us周期性记录java方法的调用栈,记录较为耗时,耗时较少不会显示在trace中。
- Java method trace: 适用于精确记录方法执行时间,即profiler的instrument模式,因一直记录方法的调用栈和cpu 使用情况,对性能影响很大,可能会造成黑屏,卡顿等情况。
- CallStack sample 适用于记录natvie+java的方法分析,Profiler的sample 模式。
- System trace: 记录同一个过程中系统各个进程的trace,适合更复杂的分析耗时。
使用方式: 先使用java method sample 记录,找出耗时的所在;接着,再用 java method trace 精确分析真正的耗时时间。
点击profiler 按钮后,等待安装后,启动后,会自动开始捕捉,等执行完需要观察的流程后,点击stop 。
打开录制的trace文件,选择main 主线程,打开如下所示:
从上面,可以清楚看到app 主线程中每个环节的方法调用栈和耗时时间等。 带颜色的方块越长,耗时越多,绿色一般是app中的代码。
选择Application的onCreate()查看,双击该方法,出现如下所示:
通过上图,可知左边菜单可知该方法耗时1s ,右边菜单top down 可知该方法的调用栈,真正耗时所在,沉睡一秒导致。
3.2 app 运行时,记录trace
有些界面是在app 特殊情况下触发,捕获该流程的trace 。可通过usb 连接手机,其次,在profiler 界面中选择可debug的进程,如下图所示:
选择cpu ,双击,如下所示:
也可以自由配置记录条件,选择不同缓存大小和周期时间us。
选择需要记录的trace类型,然后在app中使用流程,点击stop 。比如,打开一个视频页面,想了解其播放过程的耗时,如下所示:
从top down 中可清楚看到,视频页面中播放视频testPlay()的调用时间。
4.插桩使用Debug#trace api 精准记录方法执行时间
插桩方式,在release和debug 包下都可以使用,比较灵活方便。
由于存在 8MB 的缓冲区空间限制,因此 Debug API 中的 startMethodTracing(String tracePath) 方法专为时间间隔较短或难以手动启动/停止记录的场景而设计。如需设置持续时间更长的记录,请使用 Android Studio 中的性能剖析器界面。
简而言之,这种方式适合app冷启动,或者短时间内的方法记录trace。
public class TraceMethodUtils {/*** 适合于非常精确的记录,某个具体函数的耗时分析** profiler cpu 中instrument模式,会从开始一直记录方法/cpu 使用情况,对性能影响大。*/public static void startTrack(String tracFileName){Debug.startMethodTracing(fixTraceFileName(tracFileName));}/*** 在android 5.0 以后使用,间隔时间微妙 ** profiler cpu 中sample模式,系统间隔us 周期性记录调用栈,对性能影响小* @param tracFileName*/public static void startSampleTrack(String tracFileName){//1000us=1ms 周期性记录一次Debug.startMethodTracingSampling(fixTraceFileName(tracFileName),0,1000);}private static String fixTraceFileName(String tracFileName){if (!TextUtils.isEmpty(tracFileName)){tracFileName=tracFileName.endsWith(".trace")?tracFileName:tracFileName+".trace";tracFileName=new StringBuilder().append(System.currentTimeMillis()).append("_").append(tracFileName).toString();}else{tracFileName=new StringBuilder().append(System.currentTimeMillis()).append(".trace").toString();}return tracFileName;}/*** 结束跟踪*/public static void stopTrack(){Debug.stopMethodTracing();}
}
冷启动/界面启动的分析,推荐使用startSampleTrack()。
这里,具体分析某个方法testPlay(), 执行需要记录的方法,记录如下所示:
以上文件在在DeviceFileExplorer 窗口中sdcard/android/data/package名目录,双击打开该trace文件,如下所示:
使用方式:首先选择需要查看的线程,进行双击进入。因testPlay()是在主线程中执行,因此在这里选择主线程:
Bottom UP窗口
用于观察某个方法中,调用其他若干方法的耗时占比,可更进一步筛选出耗时的具体位置,选择查看比例排前的几个方法。
比如,选中testPlay(),查看其内部的耗时方法占比:创建VideoView对象,耗时最大,3002us微妙=3ms毫秒。
Flame chat 火焰图窗口:
用于倒置查看某个方法中调用若干方法的查看,很直观的看到方法调用情况与耗时情况。淡黄色的往往是需要关注的方法点,可优化点也是在这里。
Top Down窗口:
可用于查看,该方法向下调用栈,也可以清楚看到接下来代码逻辑走向。也是按耗时排列。
使用总结: 打开trace文件后–>双击选择线程–>选择观察的方法–>Top Down窗口细化查看耗时方法–>Bottom UP窗口找到耗时所在点。
Trace 存储路径和sdcard 权限问题
简单来看下trace的保存路径逻辑:
发现,trace 是默认保存在sdcard/android/data/package名目录或者sdcard根目录中,因此必须要有sdcard读写权限。trace会自动补全.trace后缀名。
官方资料:https://developer.android.com/studio/profile/record-traces?hl=zh-cn#debug-api
资料参考:
- Android Studio 中 CPU Profiler 系统性能分析工具的使用:https://juejin.cn/post/7098548053136637983
- https://juejin.cn/post/7024387231104106503
- 基于AOP思想的Android方法耗时开源分析工具TimeTracer:https://www.paincker.com/time-tracer/
- https://jishuin.proginn.com/p/763bfbd4c428
- https://zhuanlan.zhihu.com/p/508526020
- https://juejin.cn/post/7163522788781719559
- 性能优化:https://www.jianshu.com/p/837ad55f3049