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

格式:

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
<think>好的,我现在要帮助用户分析如何通过log文件解决Android应用卡顿的问题。首先,我需要回顾用户提供的引用内容,看看有哪些工具和方法已经被提到过。根据引用[1]、[3]、[4]、[5],提到了Traceview、BlockCanary、ANR日志、Systraceadb命令。这些工具都是分析卡顿的关键。 用户的主要需求是通过日志文件定位卡顿原因。首先,我应该分步骤说明不同的日志类型和工具的使用方法。比如,ANR日志可以直接从设备中获取,分析主线程是否被阻塞。然后,Traceview和Systrace可以帮助分析方法的执行时间和系统层面的性能问题。此外,BlockCanary虽然需要植入代码,但可以监控主线程的耗时操作。 接下来,我需要确保每一步都结合引用内容,并正确标注引用标记。例如,当提到Traceview时,引用[3]详细介绍了它的功能,应该在这里引用。同样,BlockCanary的缺点在引用[2]中提到,需要指出可能的性能影响。 另外,用户可能需要知道如何获取这些日志文件。例如,使用adb命令导出ANR日志(引用[5]),或者通过Systrace生成报告(引用[4])。需要详细说明每个步骤的操作方法,比如运行什么命令,如何分析结果。 还要注意用户提到的性能开销问题,比如BlockCanary可能会影响应用性能,这在引用[2]中已经说明,应该在建议使用时提到这一点,让用户权衡利弊。 最后,要确保回答结构清晰,分步骤说明,每个部分都有对应的工具和日志分析方法,并结合实际例子,如主线程的堆栈跟踪、GC频繁等常见问题。这样用户能够逐步排查卡顿原因,并找到解决方案。</think>### 通过日志文件分析Android应用卡顿问题的步骤 #### 1. **定位ANR日志** 当应用主线程阻塞超过5秒会触发ANR(Application Not Responding),系统会自动生成`/data/anr/traces.txt`文件,记录主线程堆栈信息: ```bash adb pull /data/anr/traces.txt # 导出ANR日志[^5] ``` 分析重点: - 查找主线程中耗时操作(如同步I/O、复杂计算) - 检查锁竞争或死锁现象 - 识别第三方库的阻塞调用链[^1] #### 2. **使用Traceview进行方法级分析** 通过方式记录方法执行耗时: ```java Debug.startMethodTracing("app_trace"); // 需要监控的代码段 Debug.stopMethodTracing(); ``` 生成`.trace`文件后,使用Android StudioProfiler加载分析: - 查看主线程调用树中耗时超过16ms的方法(1帧的阈值) - 关注`Wall Clock Time`与`Thread Time`的差异(I/O等待时间) - 识别重复调用的冗余方法[^3] #### 3. **Systrace系统级跟踪** 通过adb采集系统级性能数据: ```bash python systrace.py -a com.example.app -b 16384 -o trace.html gfx view res ``` 分析要点: - 检查`RenderThread`的帧生成间隔 - 观察`Choreographer#doFrame`耗时 - 识别VSYNC信号丢失情况 - 查看GC事件频率(频繁GC会导致卡顿)[^4] ![Systrace帧渲染分析示意图][^4] #### 4. **BlockCanary自动化检测** 在build.gradle中添加依赖后实现拦截器: ```java public class AppBlockCanary extends BlockCanaryContext { // 配置检测阈值和日志路径 } ``` 监控结果包含: - 卡顿时主线程堆栈快照 - 耗时操作的代码位置 - 关联的IPC调用信息[^2] #### 5. **Logcat日志过滤** 通过特定标签过滤关键信息: ```bash adb logcat -v time -s Choreographer:W ActivityManager:I *:S ``` 重点关注: ``` W/Choreographer: Skipped 3 frames! The application may be doing too much work on its main thread I/ActivityManager: ANR in com.example.app ``` #### 6. **GC日志分析** 在应用启动参数中添加: ```bash adb shell setprop log.tag.Gc DEBUG ``` 检查日志中的GC原因: ``` D/art: Explicit concurrent mark sweep GC freed... D/art: Alloc concurrent mark sweep GC freed... ``` 频繁GC(每秒超过1次)可能说明存在内存抖动[^1] ### 典型卡顿场景解决方案 1. **布局过度绘制** - 使用`Layout Inspector`检查视图层级 - 替换`RelativeLayout`为`ConstraintLayout` - 启用`GPU过度绘制调试`(设置-开发者选项) 2. **主线程IO操作** ```java new Thread(() -> { // 文件操作移至子线程 }).start(); ``` 3. **内存泄漏检测** ```bash adb shell dumpsys meminfo com.example.app -d ``` $$ T_{frame} = T_{input} + T_{measure} + T_{layout} + T_{draw} \leq 16ms $$
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值