Phân tích dựa trên điều kiện kích hoạt

ProfilingManager hỗ trợ thu thập hồ sơ dựa trên trình kích hoạt hệ thống. Hệ thống sẽ quản lý quá trình ghi và cung cấp hồ sơ kết quả cho ứng dụng của bạn.

Trình kích hoạt được liên kết với các sự kiện quan trọng về hiệu suất. Hồ sơ do hệ thống ghi lại cung cấp thông tin gỡ lỗi chi tiết cho các hành trình trọng yếu của người dùng (CUJ) liên quan đến các điều kiện kích hoạt này.

Ghi lại dữ liệu trước đây

Nhiều điều kiện kích hoạt yêu cầu phân tích dữ liệu trong quá khứ dẫn đến sự kiện. Bản thân điều kiện kích hoạt thường là hậu quả của một vấn đề chứ không phải là nguyên nhân gốc rễ. Nếu bạn chỉ bắt đầu một hồ sơ sau khi điều kiện kích hoạt xảy ra, thì nguyên nhân gốc có thể đã bị mất.

Ví dụ: một thao tác chạy trong thời gian dài trên luồng giao diện người dùng sẽ gây ra lỗi Ứng dụng không phản hồi (ANR). Khi hệ thống phát hiện lỗi ANR và báo hiệu cho ứng dụng, thao tác có thể đã hoàn tất. Việc bắt đầu một hồ sơ tại thời điểm đó sẽ bỏ lỡ công việc chặn thực tế.

Bạn không thể dự đoán chính xác thời điểm xảy ra một số điều kiện kích hoạt, nên không thể bắt đầu hồ sơ theo cách thủ công trước.

Tại sao nên sử dụng tính năng chụp dựa trên điều kiện kích hoạt?

Lý do chính để sử dụng trình kích hoạt phân tích tài nguyên là để thu thập dữ liệu cho các sự kiện không thể dự đoán, trong đó ứng dụng không thể bắt đầu ghi theo cách thủ công trước khi các sự kiện này xảy ra. Bạn có thể sử dụng trình kích hoạt phân tích tài nguyên để:

  • Gỡ lỗi các vấn đề về hiệu suất: Chẩn đoán lỗi ANR, rò rỉ bộ nhớ và các vấn đề khác về độ ổn định.
  • Tối ưu hoá hành trình trọng yếu của người dùng: Phân tích và cải thiện các luồng, chẳng hạn như quá trình khởi động ứng dụng.
  • Tìm hiểu hành vi của người dùng: Nhận thông tin chi tiết về các sự kiện, chẳng hạn như người dùng thoát khỏi ứng dụng.

Thiết lập điều kiện kích hoạt

Đoạn mã sau đây minh hoạ cách đăng ký điều kiện kích hoạt TRIGGER_TYPE_APP_FULLY_DRAWN và áp dụng giới hạn tốc độ cho điều kiện kích hoạt đó.

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);

}

Mã này thực hiện các bước sau:

  1. Lấy trình quản lý: Truy xuất dịch vụ ProfilingManager.
  2. Xác định điều kiện kích hoạt: Tạo ProfilingTrigger cho TRIGGER_TYPE_APP_FULLY_DRAWN. Sự kiện này xảy ra khi ứng dụng báo cáo rằng ứng dụng đã khởi động xong và có thể tương tác.
  3. Đặt giới hạn số lượng: Áp dụng giới hạn số lượng 1 giờ cho điều kiện kích hoạt cụ thể này (setRateLimitingPeriodHours(1)). Điều này ngăn ứng dụng ghi lại nhiều hồ sơ khởi động mỗi giờ.
  4. Đăng ký trình nghe: Gọi registerForAllProfilingResults để xác định lệnh gọi lại xử lý kết quả. Lệnh gọi lại này nhận đường dẫn của hồ sơ đã lưu thông qua getResultFilePath().
  5. Thêm điều kiện kích hoạt: Đăng ký danh sách điều kiện kích hoạt với ProfilingManager bằng addProfilingTriggers.
  6. Sự kiện kích hoạt: Gọi reportFullyDrawn(), phát ra sự kiện TRIGGER_TYPE_APP_FULLY_DRAWN đến hệ thống kích hoạt một tập hợp hồ sơ giả định rằng dấu vết nền hệ thống đang chạy và có hạn mức giới hạn tốc độ. Bước không bắt buộc này minh hoạ một quy trình toàn diện vì ứng dụng của bạn phải gọi reportFullyDrawn() cho điều kiện kích hoạt này.

Truy xuất dấu vết

Hệ thống lưu hồ sơ dựa trên điều kiện kích hoạt trong cùng thư mục với các hồ sơ khác. Tên tệp cho dấu vết được kích hoạt tuân theo định dạng sau:

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

Bạn có thể lấy tệp bằng ADB. Ví dụ: để lấy dấu vết hệ thống được ghi lại bằng mã ví dụ bằng ADB, bạn có thể làm như sau:

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

Để biết thông tin chi tiết về cách trực quan hoá các dấu vết này, hãy xem phần Truy xuất và phân tích dữ liệu phân tích tài nguyên.

Cách hoạt động của tính năng theo dõi trong nền

Để thu thập dữ liệu trước khi có điều kiện kích hoạt, hệ điều hành sẽ định kỳ bắt đầu một dấu vết nền. Nếu một điều kiện kích hoạt xảy ra trong khi dấu vết nền này đang hoạt động và ứng dụng của bạn đã đăng ký điều kiện kích hoạt đó, thì hệ thống sẽ lưu hồ sơ dấu vết vào thư mục của ứng dụng. Sau đó, hồ sơ sẽ bao gồm thông tin dẫn đến điều kiện kích hoạt.

Sau khi lưu hồ sơ, hệ thống sẽ thông báo cho ứng dụng của bạn bằng lệnh gọi lại được cung cấp cho registerForAllProfilingResults. Lệnh gọi lại này cung cấp đường dẫn đến hồ sơ đã thu thập. Bạn có thể truy cập vào hồ sơ này bằng cách gọi ProfilingResult#getResultFilePath().

Sơ đồ cho thấy cách hoạt động của ảnh chụp nhanh dấu vết trong nền, với vùng đệm vòng thu thập dữ liệu trước một sự kiện kích hoạt.
Hình 1: Cách hoạt động của ảnh chụp nhanh dấu vết trong nền.

Để giảm tác động đến hiệu suất thiết bị và thời lượng pin, hệ thống không chạy dấu vết nền liên tục. Thay vào đó, báo cáo này sử dụng phương pháp lấy mẫu. Hệ thống sẽ bắt đầu một dấu vết nền một cách ngẫu nhiên trong một khung thời gian đã đặt (với thời lượng tối thiểu và tối đa). Việc giãn cách ngẫu nhiên các dấu vết này sẽ cải thiện phạm vi kích hoạt.

Hồ sơ do hệ thống kích hoạt có kích thước tối đa do hệ thống xác định, vì vậy, các hồ sơ này sử dụng vùng đệm vòng. Khi vùng đệm đầy, dữ liệu theo dõi mới sẽ ghi đè dữ liệu cũ nhất. Như minh hoạ trong Hình 1, dấu vết đã ghi lại có thể không bao gồm toàn bộ thời lượng của bản ghi trong nền nếu vùng đệm đã đầy; thay vào đó, dấu vết này thể hiện hoạt động gần đây nhất dẫn đến điều kiện kích hoạt.

Triển khai giới hạn số lần gọi theo điều kiện kích hoạt

Trình kích hoạt tần suất cao có thể nhanh chóng tiêu thụ hạn mức của bộ giới hạn tốc độ trong ứng dụng. Để hiểu rõ hơn về bộ giới hạn tốc độ, bạn nên xem phần Cách hoạt động của bộ giới hạn tốc độ. Để ngăn một loại điều kiện kích hoạt duy nhất làm cạn kiệt hạn mức, bạn có thể triển khai giới hạn tốc độ theo điều kiện kích hoạt cụ thể.

ProfilingManager hỗ trợ giới hạn số lần gọi theo trình kích hoạt do ứng dụng xác định. Điều này cho phép bạn thêm một lớp điều tiết dựa trên thời gian khác ngoài bộ giới hạn tốc độ hiện có. Sử dụng API setRateLimitingPeriodHours để đặt thời gian hồi chiêu cụ thể cho một điều kiện kích hoạt. Sau khi hết thời gian hồi chiêu, bạn có thể kích hoạt lại.

Gỡ lỗi trình kích hoạt trên máy

Vì dấu vết nền chạy vào những thời điểm ngẫu nhiên, nên việc gỡ lỗi trình kích hoạt trên thiết bị là rất khó. Để buộc theo dõi trong nền cho mục đích kiểm thử, hãy sử dụng lệnh ADB sau:

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

Lệnh này buộc hệ thống bắt đầu theo dõi liên tục trong nền cho gói được chỉ định, cho phép mọi điều kiện kích hoạt có thể thu thập hồ sơ nếu bộ giới hạn tốc độ cho phép.

Bạn cũng có thể bật các tuỳ chọn gỡ lỗi khác, chẳng hạn như tắt bộ giới hạn tốc độ khi gỡ lỗi cục bộ. Để biết thêm thông tin, hãy xem phần Lệnh gỡ lỗi để lập hồ sơ cục bộ.