基于触发器的分析

ProfilingManager 支持根据系统触发器捕获配置文件。系统会管理记录过程,并向您的应用提供生成的配置文件。

触发器与对性能要求苛刻的事件相关联。系统记录的配置文件可为与这些触发器关联的关键用户历程 (CUJ) 提供详细的调试信息。

捕获历史数据

许多触发器都需要分析事件发生之前的历史数据。触发本身通常是问题的结果,而不是根本原因。如果您仅在触发器触发后才开始分析,则可能已经错过了根本原因。

例如,界面线程上的长时间运行操作会导致出现应用无响应 (ANR) 错误。在系统检测到 ANR 并向应用发出信号时,该操作可能已完成。在该时刻开始分析会导致错过实际的阻塞工作。

准确预测某些触发器何时触发是不切实际的,因此无法提前手动开始性能剖析。

为何要使用基于触发的捕获?

使用分析触发器的主要原因是,捕获不可预测事件的数据,因为在这些事件发生之前,应用无法手动开始记录。分析触发器可用于:

  • 调试性能问题:诊断 ANR、内存泄漏和其他稳定性问题。
  • 优化关键用户历程:分析并改进流程,例如应用启动。
  • 了解用户行为:深入了解事件,例如用户主动退出应用。

设置触发器

以下代码演示了如何注册 TRIGGER_TYPE_APP_FULLY_DRAWN 触发器并对其应用速率限制。

Kotlin

fun recordWithTrigger() {
    val profilingManager = applicationContext.getSystemService(ProfilingManager::class.java)

    val triggers = ArrayList<ProfilingTrigger>()

    val triggerBuilder = ProfilingTrigger.Builder(ProfilingTrigger.TRIGGER_TYPE_APP_FULLY_DRAWN)
        .setRateLimitingPeriodHours(1)

    triggers.add(triggerBuilder.build())

    val mainExecutor: Executor = Executors.newSingleThreadExecutor()

    val resultCallback = Consumer<ProfilingResult> { profilingResult ->
        if (profilingResult.errorCode == ProfilingResult.ERROR_NONE) {
            Log.d(
                "ProfileTest",
                "Received profiling result file=" + profilingResult.resultFilePath
            )
            setupProfileUploadWorker(profilingResult.resultFilePath)
        } else {
            Log.e(
                "ProfileTest",
                "Profiling failed errorcode=" + profilingResult.errorCode + " errormsg=" + profilingResult.errorMessage
            )
        }
    }

    profilingManager.registerForAllProfilingResults(mainExecutor, resultCallback)
    profilingManager.addProfilingTriggers(triggers)

Java

public void recordWithTrigger() {
  ProfilingManager profilingManager = getApplicationContext().getSystemService(
      ProfilingManager.class);
  List<ProfilingTrigger> triggers = new ArrayList<>();
  ProfilingTrigger.Builder triggerBuilder = new ProfilingTrigger.Builder(
      ProfilingTrigger.TRIGGER_TYPE_APP_FULLY_DRAWN);
  triggerBuilder.setRateLimitingPeriodHours(1);
  triggers.add(triggerBuilder.build());

  Executor mainExecutor = Executors.newSingleThreadExecutor();
  Consumer<ProfilingResult> resultCallback =
      new Consumer<ProfilingResult>() {
        @Override
        public void accept(ProfilingResult profilingResult) {
          if (profilingResult.getErrorCode() == ProfilingResult.ERROR_NONE) {
            Log.d(
                "ProfileTest",
                "Received profiling result file=" + profilingResult.getResultFilePath());
            setupProfileUploadWorker(profilingResult.getResultFilePath());
          } else {
            Log.e(
                "ProfileTest",
                "Profiling failed errorcode="
                    + profilingResult.getErrorCode()
                    + " errormsg="
                    + profilingResult.getErrorMessage());
          }
        }
      };
  profilingManager.registerForAllProfilingResults(mainExecutor, resultCallback);
  profilingManager.addProfilingTriggers(triggers);

该代码执行以下步骤:

  1. 获取管理器:检索 ProfilingManager 服务。
  2. 定义触发器:为 TRIGGER_TYPE_APP_FULLY_DRAWN 构建 ProfilingTrigger。当应用报告已完成启动并可进行互动时,会发生此事件。
  3. 设置速率限制:对此特定触发器 (setRateLimitingPeriodHours(1)) 应用 1 小时的速率限制。这可防止应用每小时记录多个启动配置文件。
  4. 注册监听器:调用 registerForAllProfilingResults 来定义处理结果的回调。此回调通过 getResultFilePath() 接收已保存配置文件的路径。
  5. 添加触发器:使用 addProfilingTriggersProfilingManager 注册触发器列表。
  6. 触发事件:调用 reportFullyDrawn(),向系统发出 TRIGGER_TYPE_APP_FULLY_DRAWN 事件,触发配置文件收集,前提是系统后台跟踪正在运行且有可用的速率限制器配额。此可选步骤演示了端到端流程,因为您的应用必须为此触发器调用 reportFullyDrawn()

检索轨迹

系统会将基于触发条件的配置文件保存在与其他配置文件相同的目录中。触发的轨迹的文件名采用以下格式:

profile_trigger_<profile_type_code>_<datetime>.<profile-type-name>

您可以使用 ADB 拉取该文件。例如,如需使用 ADB 拉取通过示例代码捕获的系统轨迹,命令可能如下所示:

adb pull /data/user/0/com.example.sampleapp/files/profiling/profile_trigger_1_2025-05-06-14-12-40.perfetto-trace

如需详细了解如何直观呈现这些轨迹,请参阅检索和分析分析数据

后台轨迹跟踪功能的运作方式

为了捕获触发器之前的数据,操作系统会定期启动后台轨迹。如果触发器在后台轨迹处于有效状态时触发,并且您的应用已注册该触发器,系统会将轨迹配置文件保存到您的应用目录中。然后,配置文件将包含导致触发的信息。

保存配置文件后,系统会使用提供给 registerForAllProfilingResults 的回调通知您的应用。此回调提供捕获的性能剖析文件的路径,可通过调用 ProfilingResult#getResultFilePath() 来访问该路径。

图表:显示了后台轨迹快照的工作方式,其中环形缓冲区在触发事件之前捕获数据。
图 1:后台轨迹快照的工作方式。

为减少对设备性能和电池续航时间的影响,系统不会持续运行后台轨迹。而是使用抽样方法。系统会在设定的时间范围内(具有最短和最长时间)随机开始后台跟踪。随机间隔这些轨迹可提高触发覆盖率。

系统触发的配置文件具有系统定义的最大大小,因此它们使用环形缓冲区。缓冲区填满后,新的轨迹数据会覆盖最旧的数据。如图 1 所示,如果缓冲区已满,捕获的轨迹可能无法涵盖整个后台录制时长;相反,它表示触发前最近的活动。

实现特定于触发器的速率限制

高频触发器会快速消耗应用的速率限制器配额。为了更好地了解速率限制器,我们建议您参阅速率限制器的工作原理。为防止单一触发器类型用尽您的配额,您可以实现特定于触发器的速率限制。

ProfilingManager 支持应用定义的特定于触发器的速率限制。这样一来,除了现有的速率限制器之外,您还可以添加另一层基于时间的限制。使用 setRateLimitingPeriodHours API 为触发器设置特定的冷却时间。冷却时间结束后,您可以再次触发该功能。

在本地调试触发器

由于后台轨迹是在随机时间运行的,因此很难在本地调试触发器。如需强制执行后台轨迹以进行测试,请使用以下 ADB 命令:

adb shell device_config put profiling_testing system_triggered_profiling.testing_package_name <com.example.myapp>

此命令会强制系统为指定软件包启动连续的后台跟踪,从而允许每个触发器在速率限制器允许的情况下收集配置文件。

您还可以启用其他调试选项,例如在本地调试时停用速率限制器。如需了解详情,请参阅用于本地分析的调试命令