跳至主内容
版本:当前版本

性能分析

非官方测试版翻译

本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →

性能分析是通过剖析应用程序的性能、资源使用和行为,识别潜在瓶颈或低效问题的过程。利用性能分析工具确保应用在不同设备和条件下流畅运行至关重要。

对于 iOS 平台,Instruments 是不可或缺的工具;对于 Android,你应该掌握 Android Studio Profiler 的使用方法。

但首先,请确保开发模式已关闭!

使用系统追踪分析 Android UI 性能

Android 支持超过 1 万种不同设备,其通用化框架设计需要兼顾软件渲染:这种架构特性及对多种硬件目标的兼容要求,意味着相比 iOS 你能"免费"获得的性能优化更少。但有时仍有改进空间——而且很多时候问题根本不在原生代码上!

调试卡顿问题的第一步是定位每个 16ms 帧周期中的时间消耗点。为此我们将使用 Android Studio 内置的系统追踪分析器

备注

独立 systrace 工具已从 Android platform-tools 中移除。请改用 Android Studio Profiler,它提供相同的功能且用户界面更友好。

1. 收集追踪记录

首先通过 USB 连接出现卡顿问题的设备。在 Android Studio 中打开项目的 android 文件夹,在右上角设备面板中选择目标设备,并以可分析模式运行项目

当应用以可分析模式在设备运行后,将其导航至需要分析的界面/动画触发前状态,然后在 Android Studio Profiler 面板中启动"捕获系统活动"任务

追踪开始后立即执行目标动画或交互操作,完成后点击"Stop recording"。此时可直接在 Android Studio 中分析追踪记录。也可在"Past Recordings"面板中选择记录,点击"Export recording"后使用 Perfetto 等工具打开。

2. 解读追踪记录

在 Android Studio 或 Perfetto 中打开追踪文件后,你将看到类似以下内容:

示例

提示

使用 WASD 键进行视图平移和缩放

不同工具的界面可能略有差异,但以下解读方法通用。

启用 VSync 高亮显示

勾选界面右上角的复选框以高亮 16ms 帧边界:

启用 VSync 高亮显示

此时应出现上图中的斑马纹。如未显示,请尝试更换分析设备:三星设备曾出现 VSync 显示问题,Nexus 系列通常更可靠。

3. 定位应用进程

滚动查找包含应用包名(或部分名称)的进程。本例中分析的 com.facebook.adsmanager 因内核线程名称限制显示为 book.adsmanager

左侧线程列表对应右侧时间轴行。我们需要关注这几个关键线程:UI 线程(显示应用包名或"UI Thread")、mqt_jsmqt_native_modules。若在 Android 5+ 设备运行,还需关注 Render Thread。

  • UI 线程。 这里是标准 Android 测量/布局/绘制发生的地方。右侧的线程名称将是你的应用包名(我的例子中是 book.adsmanager)或 UI Thread。该线程中的事件通常与 ChoreographertraversalsDispatchUI 相关:

    UI Thread Example

  • JS 线程。 这里是 JavaScript 执行的地方。线程名称会是 mqt_js<...>(取决于设备内核的配合程度)。若未显示名称,可通过查找 JSCallBridge.executeJSCall 等特征来识别:

    JS Thread Example

  • 原生模块线程。 这里是原生模块调用(如 UIManager)执行的地方。线程名称会是 mqt_native_modules<...>。若未显示名称,可通过查找 NativeCallcallJavaModuleMethodonBatchComplete 等特征来识别:

    Native Modules Thread Example

  • 额外提示:渲染线程。 如果你使用 Android L (5.0) 及以上版本,应用中还会有渲染线程。该线程生成实际用于绘制 UI 的 OpenGL 指令。线程名称会是 RenderThread<...>。若未显示名称,可通过查找 DrawFramequeueBuffer 等特征来识别:

    Render Thread Example

确定问题根源

流畅的动画应该如下图所示:

Smooth Animation

每次颜色变化代表一帧——请记住,为了显示一帧,所有 UI 工作都必须在 16 毫秒内完成。注意没有任何线程的工作接近帧边界。这样渲染的应用能达到 60 FPS。

但如果你观察到卡顿,可能会看到类似这样的情况:

Choppy Animation from JS

注意 JS 线程几乎持续执行,且跨越了帧边界!这个应用无法达到 60 FPS。此时问题出在 JS 部分

你也可能看到这样的情况:

Choppy Animation from UI

这种情况下,UI 和渲染线程的工作跨越了帧边界。每帧尝试渲染的 UI 需要完成的工作量过大。此时问题出在原生视图渲染

至此,你已经获得了指导后续步骤的关键信息。

解决 JavaScript 问题

如果确定是 JS 问题,请在具体执行的 JS 代码中寻找线索。在上面的场景中,我们看到每帧多次调用 RCTEventEmitter。这是该跟踪中 JS 线程的放大视图:

Too much JS

这显然不合理。为何会被如此频繁调用?是否确实是不同事件?这些问题的答案通常取决于产品代码。多数情况下,你需要检查 shouldComponentUpdate 的实现。

解决原生 UI 问题

如果确定是原生 UI 问题,通常有两种情况:

  1. 每帧绘制的 UI 在 GPU 上涉及过多工作,或者

  2. 在动画或交互过程中构建新的 UI(例如在滚动过程中加载新内容)。

GPU 负载过高

在第一种情况下,你会看到 UI 线程和/或渲染线程的轨迹呈现如下状态:

Overloaded GPU

注意 DrawFrame 中花费的长时间跨越了多个帧边界。这段时间实际上是 GPU 在处理前一帧命令缓冲区时的等待耗时。

缓解此问题的方法包括:

  • 对正在执行动画/变换的复杂静态内容(如 Navigator 的滑动/透明度动画)尝试使用 renderToHardwareTextureAndroid

  • 确保没有启用默认关闭的 needsOffscreenAlphaCompositing,该选项在多数情况下会显著增加 GPU 的每帧负载

在 UI 线程创建新视图

在第二种情况下,你会看到类似下图的轨迹:

Creating Views

注意首先是 JS 线程进行短暂运算,然后在原生模块线程执行任务,最后在 UI 线程进行高开销的视图遍历操作

除非能将新 UI 的构建推迟到交互之后或简化 UI 结构,否则没有快速解决方案。React Native 团队正在开发基础设施级方案,实现在主线程外创建配置新 UI,从而保持交互流畅性

定位原生 CPU 热点

若问题出现在原生代码端,可使用 CPU 热点分析器获取详细数据。打开 Android Studio Profiler 面板并选择 "Find CPU Hotspots (Java/Kotlin Method Recording)"

选择 Java/Kotlin 记录

务必选择"查找 CPU 热点**(Java/Kotlin 记录)**"而非"查找 CPU 热点(调用栈采样)",二者图标相似但功能不同

执行交互操作后点击"停止记录"。由于记录过程资源消耗较大,请保持简短交互。随后可在 Android Studio 中分析结果轨迹,或导出至 Firefox Profiler 等在线工具查看

与系统跟踪不同,CPU 热点分析速度较慢,无法提供精确测量。但它能揭示每帧中调用的原生方法及时间消耗比例分布