在上一篇文章 Android 性能优化(五)使用 Heap Dump 分析内存 中,我们使用 Android Profiler 工具中的 Heap Dump 来分析内存。这里将使用如下图中 Android Profiler 工具中的 Callstacks Sample 和 Java/Kotlin Method Recording 来分析方法的耗时。

Callstacks Sample 和 Java/Kotlin Method Recording 的区别
在 Android studio 中,Callstacks Sample 和 Java/Kotlin Method Recording 的界面都是下图所示:

可以看到这两个工具的功能是相同的。Callstacks Sample 和 Java/Kotlin Method Recording 的区别主要是:Callstacks Sample 会包括 native 调用堆栈信息,因此更加全面,而Java/Kotlin Method Recording只包含调用的 Java/Kotlin 方法的堆栈信息。
除此之外,如下图所示 Java/Kotlin Method Recording 还支持选择记录类型:

其中:
- Tracing : 在运行时检测应用,从而在每个方法调用开始和结束时记录一个时间戳。系统会收集并比较这些时间戳,以生成方法跟踪数据,包括时间信息。
- Sampling (legacy) :旧版实现,在应用的 Java 或 Kotlin 代码执行期间,频繁捕获应用的调用堆栈(即采样获取)。性能分析器会比较捕获的数据集,以推导与应用的 Java 或 Kotlin 代码执行有关的时间和资源使用信息。
如果我们关心正在调用的确切方法,则应使用 Tracing ;如果我们更关心时间,而不是被调用的确切方法,则应使用Sampling。
注意由于Tracing是一项密集型流程,因此如果我们使用此选项,最好将录制时长控制在 5 秒左右或更短。
Callstacks Sample 和 Java/Kotlin Method Recording 的设置

如上图所示,点击右上角的设置按钮,进入 Profiler 的设置界面。

Callstack Sample 的设置是其采样的周期时间

Java/Kotlin Method Trace 的设置是限制输出 trace 数据的大小。当设备在 Android 8 及以上时,系统会忽略文件大小限制值

Java/Kotlin Method Sample 的设置是采样频率和数据大小限制。当设备在 Android 8 及以上时,系统会忽略文件大小限制值
Profileable app 和 Debuggable app
Profileable app 是可分析应用;Debuggable app 是可调试应用。它们的区别是,Profileable app 可以用于性能分析,但是不能调试,因此减少了由于调试导致的性能方面的开销;而 Debuggable app 即可调试,也可用于性能分析,但是会多出一些调试带来的性能开销。
Callstacks Sample 和 Java/Kotlin Method Recording 建议采用 Profileable app 来做性能分析。启动 Profileable app 的方法有两种。第一种方法是:

如上图所示,在更多里面选中 Profile: Run app as profileable
第二种方式是:

选中 Process start 开始性能分析
Callstacks Sample 和 Java/Kotlin Method Recording 的使用

收起帧
如上图所示,左上角有一个 Collapse frames 按钮。点击该按钮,可以按照需要隐藏的帧类型。如下图所示:

一般情况下,我们需要收起来自 Java 虚拟机(例如 android::AndroidRuntime::start
和 art::{...}
)和系统内核(例如 [kernel.kallsyms]+{offset}
)的帧。通常,这对应于收起与 [kernel.kallsyms]
、/apex/
和 /system/*
相关的帧。
CPU Usage
在这个界面,我们可以看到 CPU 的使用情况。还可以选中指定区别,来看当前区域的方法调用的耗时
Interaction
Interaction(互动):沿着时间轴显示用户互动和应用生命周期事件(需要可调试的应用进程和搭载 API 级别 26 或更高版本的设备)。
Threads
在 Threads 中显示了对应线程中函数轨迹,一般我们看第一个线程,如下图所示。

在图中显示的是 Call Chart 。Call Chart 以图形方式来呈现方法轨迹或函数轨迹,其中调用的时间段和时间在横轴上表示,而其被调用方则在纵轴上显示。对系统 API 的调用显示为橙色,对应用自有方法的调用显示为绿色,对第三方 API(包括 Java 语言 API)的调用显示为蓝色。下图显示了一个调用图表示例,说明了给定方法或函数的 Self 时间、Children 时间和 Total 时间的概念。

选中线程后,可以在右边的 Analysis 窗格看到当前线程的分析结果。如下所示:

Analysis 窗格
在Analysis 窗格,有 Flame Chart 、Top Down 、Bottom Up 和 Events 四种标签页需要着重了解。
Flame Chart
Flame Chart(火焰图) 提供一个倒置的调用图表,用来汇总完全相同的调用堆栈。也就是说,将具有相同调用方顺序的完全相同的方法或函数收集起来,并在火焰图中将它们表示为一个较长的横条。如下图所示:

其中B1、B2 和 B3 具有相同的调用方顺序 (A → D → B),因此系统将它们汇总在一起。同样,C1 和 C3 也汇总在一起,因为它们也具有相同的调用方顺序 (A → D → B → C)。请注意,C2 不包括在内,因为它具有不同的调用方顺序 (A → D → C)。如下图所示:

Top Down 和 Bottom Up
Top Down 标签页显示一个调用列表,在该列表中展开方法或函数节点会显示它的被调用方;Bottom Up 标签页显示一个调用列表,在该列表中展开函数或方法的节点会显示它的调用方。

Events
Events表格列出了当前所选线程中的所有调用。通过选择表格中的某一行,我们可以在时间轴上导航到所选调用的开始时间和结束时间。这样,我们就可以在时间轴上准确定位事件。

其中:
- start time:是启动时的时间点
- Name:调用名字
- Wall Duration:总实际耗时(真实时间)
- Self Time:自身逻辑的实际耗时(不含子方法)
- CPU Duration:总 CPU 占用时间(不含等待)
- CPU Self Time:自身逻辑的 CPU 占用时间(不含子方法)