基于触发器的分析

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. 设置速率限制:对此特定触发器应用 1 小时的速率限制 (setRateLimitingPeriodHours(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>

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

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