Pembuatan profil berbasis pemicu

ProfilingManager mendukung pengambilan profil berdasarkan pemicu sistem. Sistem mengelola proses perekaman dan memberikan profil yang dihasilkan ke aplikasi Anda.

Pemicu terkait dengan peristiwa penting performa. Profil yang direkam sistem memberikan informasi proses debug yang mendetail untuk perjalanan penting pengguna (CUJ) yang terkait dengan pemicu ini.

Mengambil data historis

Banyak pemicu memerlukan analisis data historis yang mengarah ke peristiwa tersebut. Pemicunya sendiri sering kali merupakan konsekuensi dari suatu masalah, bukan penyebab utamanya. Jika Anda memulai profil hanya setelah pemicu terjadi, penyebab utamanya mungkin sudah hilang.

Misalnya, operasi yang berjalan lama di UI thread akan menyebabkan error Aplikasi Tidak Merespons (ANR). Pada saat sistem mendeteksi ANR dan memberi sinyal ke aplikasi, operasi mungkin telah selesai. Memulai profil pada saat itu akan melewatkan pekerjaan pemblokiran yang sebenarnya.

Memprediksi secara tepat kapan beberapa pemicu terjadi tidaklah memungkinkan, sehingga tidak mungkin untuk memulai profil secara manual terlebih dahulu.

Mengapa menggunakan pengambilan berbasis pemicu?

Alasan utama penggunaan pemicu pembuatan profil adalah untuk mengambil data untuk peristiwa yang tidak dapat diprediksi, yang membuat aplikasi tidak mungkin mulai merekam secara manual sebelum peristiwa ini terjadi. Pemicu pembuatan profil dapat digunakan untuk:

  • Men-debug masalah performa: Mendiagnosis ANR, kebocoran memori, dan masalah stabilitas lainnya.
  • Mengoptimalkan perjalanan penting pengguna: Menganalisis dan meningkatkan alur, misalnya, startup aplikasi.
  • Memahami perilaku pengguna: Dapatkan insight tentang peristiwa, misalnya, keluar dari aplikasi yang dimulai oleh pengguna.

Menyiapkan pemicu

Kode berikut menunjukkan cara mendaftar untuk pemicu TRIGGER_TYPE_APP_FULLY_DRAWN dan menerapkan pembatasan kapasitas ke pemicu tersebut.

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

}

Kode ini melakukan langkah-langkah berikut:

  1. Mendapatkan pengelola: Mengambil layanan ProfilingManager.
  2. Tentukan pemicu: Mem-build ProfilingTrigger untuk TRIGGER_TYPE_APP_FULLY_DRAWN. Peristiwa ini terjadi saat aplikasi melaporkan bahwa aplikasi telah selesai memulai dan bersifat interaktif.
  3. Set rate limits: Menerapkan pembatasan kapasitas 1 jam ke pemicu spesifik ini (setRateLimitingPeriodHours(1)). Hal ini mencegah aplikasi merekam lebih dari satu profil startup per jam.
  4. Daftarkan pemroses: Memanggil registerForAllProfilingResults untuk menentukan callback yang menangani hasil. Callback ini menerima jalur profil yang disimpan melalui getResultFilePath().
  5. Menambahkan pemicu: Mendaftarkan daftar pemicu dengan ProfilingManager menggunakan addProfilingTriggers.
  6. Peristiwa pengaktifan: Memanggil reportFullyDrawn(), yang memunculkan peristiwa TRIGGER_TYPE_APP_FULLY_DRAWN ke sistem yang memicu pengumpulan profil dengan asumsi pelacakan latar belakang sistem sedang berjalan dan ada kuota pembatas kapasitas yang tersedia. Langkah opsional ini menunjukkan alur menyeluruh karena aplikasi Anda harus memanggil reportFullyDrawn() untuk pemicu ini.

Mengambil rekaman aktivitas

Sistem menyimpan profil berbasis pemicu di direktori yang sama dengan profil lainnya. Nama file untuk rekaman aktivitas yang dipicu mengikuti format berikut:

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

Anda dapat menarik file menggunakan ADB. Misalnya, untuk mengambil pelacakan sistem yang direkam dengan kode contoh menggunakan ADB, kodenya mungkin terlihat seperti ini:

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

Untuk mengetahui detail tentang cara memvisualisasikan rekaman aktivitas ini, lihat Mengambil dan menganalisis data pembuatan profil.

Cara kerja pelacakan latar belakang

Untuk mengambil data dari sebelum pemicu, OS secara berkala memulai pelacakan latar belakang. Jika pemicu terjadi saat rekaman aktivitas latar belakang ini aktif dan aplikasi Anda terdaftar untuk pemicu tersebut, sistem akan menyimpan profil rekaman aktivitas ke direktori aplikasi Anda. Profil kemudian akan menyertakan informasi yang mengarah ke pemicu.

Setelah profil disimpan, sistem akan memberi tahu aplikasi Anda menggunakan callback yang diberikan ke registerForAllProfilingResults. Callback ini menyediakan jalur ke profil yang diambil yang dapat diakses dengan memanggil ProfilingResult#getResultFilePath().

Diagram yang menunjukkan cara kerja snapshot pelacakan latar belakang, dengan ring buffer yang menangkap data sebelum peristiwa pemicu.
Gambar 1: Cara kerja snapshot rekaman aktivitas latar belakang.

Untuk mengurangi dampak pada performa perangkat dan masa pakai baterai, sistem tidak menjalankan pelacakan latar belakang secara terus-menerus. Sebagai gantinya, Google Analytics menggunakan metode pengambilan sampel. Sistem secara acak memulai pelacakan latar belakang dalam jangka waktu yang ditetapkan (dengan durasi minimum dan maksimum). Memberi jarak secara acak pada trace ini akan meningkatkan cakupan pemicu.

Profil yang dipicu sistem memiliki ukuran maksimum yang ditentukan sistem, sehingga menggunakan ring buffer. Setelah buffer penuh, data rekaman aktivitas baru akan menimpa data terlama. Seperti yang ditunjukkan pada Gambar 1, rekaman aktivitas yang diambil mungkin tidak mencakup seluruh durasi perekaman latar belakang jika buffer terisi; melainkan, rekaman aktivitas tersebut mewakili aktivitas terbaru yang mengarah ke pemicu.

Menerapkan pembatasan kapasitas khusus pemicu

Pemicu frekuensi tinggi dapat dengan cepat menghabiskan kuota pembatas kapasitas aplikasi Anda. Untuk lebih memahami pembatas kapasitas, sebaiknya Anda melihat Cara kerja pembatas kapasitas. Untuk mencegah satu jenis pemicu menghabiskan kuota Anda, Anda dapat menerapkan pembatasan kapasitas khusus pemicu.

ProfilingManager mendukung pembatasan kapasitas khusus pemicu yang ditentukan aplikasi. Dengan demikian, Anda dapat menambahkan lapisan throttling berbasis waktu lain selain pembatas kapasitas yang ada. Gunakan API setRateLimitingPeriodHours untuk menetapkan waktu tunggu tertentu untuk pemicu. Setelah cooldown berakhir, Anda dapat memicunya lagi.

Men-debug pemicu secara lokal

Karena trace latar belakang berjalan pada waktu acak, proses debug pemicu secara lokal sulit dilakukan. Untuk memaksa pelacakan latar belakang untuk pengujian, gunakan perintah ADB berikut:

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

Perintah ini memaksa sistem untuk memulai pelacakan latar belakang berkelanjutan untuk paket yang ditentukan, sehingga setiap pemicu dapat mengumpulkan profil jika pembatas kapasitas mengizinkan.

Anda juga dapat mengaktifkan opsi debug lainnya, misalnya, menonaktifkan pembatas kapasitas saat melakukan proses debug secara lokal. Untuk mengetahui informasi selengkapnya, lihat Perintah debug untuk pembuatan profil lokal.