Android 耗时分析(adb shell/Studio CPU Profiler/插桩Trace API)

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的时间

格式:

shell 复制代码
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。

java 复制代码
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

资料参考

相关推荐
simplepeng6 小时前
我们都知道但总是忽略的5个Jetpack Compose细节
android·android jetpack
刮风那天7 小时前
Android 如何提高进程优先级避免被查杀?
android
萑澈7 小时前
如何在Rocky Linux 8单节点集群上安装Apache Hadoop
adb
修行者对6668 小时前
安卓阿里云镜像
android
刮风那天9 小时前
Android AMS创建进程不用Binder而用Socket?
android·java·binder
知行合一。。。11 小时前
Python--05--面向对象(继承,多态)
android·开发语言·python
张小潇12 小时前
AOSP15 WMS/AMS系统开发 -窗口动画源码分析
android
程序员陆业聪13 小时前
Shadow核心原理:壳子Activity与代理机制的精妙设计
android
plainGeekDev14 小时前
Android 开发者再不转Kotlin,真的来不及了
android·kotlin
赏金术士14 小时前
第五章:数据层—网络请求与Repository
android·kotlin·compose