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 pen-debug-an 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 masalah, bukan akar penyebabnya. Jika Anda memulai pembuatan profil hanya setelah pemicu terjadi, penyebab utama mungkin sudah hilang.

Misalnya, operasi yang berjalan lama di UI thread menyebabkan error Application Not Responding (ANR). Pada saat sistem mendeteksi ANR dan memberi sinyal pada aplikasi, operasi mungkin telah selesai. Memulai profil pada saat itu tidak akan memblokir pekerjaan yang sebenarnya.

Memprediksi secara tepat kapan beberapa pemicu terjadi tidak mungkin dilakukan, sehingga tidak mungkin memulai profil secara manual sebelumnya.

Mengapa menggunakan pengambilan berbasis pemicu?

Alasan utama penggunaan pemicu pembuatan profil adalah untuk merekam data untuk peristiwa yang tidak dapat diprediksi, yang membuat aplikasi tidak dapat memulai perekaman 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 kualitas alur, misalnya, peluncuran aplikasi.
  • Memahami perilaku pengguna: Dapatkan insight tentang peristiwa, misalnya, keluar dari aplikasi yang dimulai pengguna.

Menyiapkan pemicu

Kode berikut menunjukkan cara mendaftar untuk pemicu TRIGGER_TYPE_APP_FULLY_DRAWN dan menerapkan pembatasan kecepatan padanya.

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. Dapatkan pengelola: Mengambil layanan ProfilingManager.
  2. Menentukan pemicu: Membangun ProfilingTrigger untuk TRIGGER_TYPE_APP_FULLY_DRAWN. Peristiwa ini terjadi saat aplikasi melaporkan bahwa aplikasi telah menyelesaikan proses startup dan bersifat interaktif.
  3. Menetapkan batas frekuensi: Menerapkan batas frekuensi 1 jam ke pemicu tertentu ini (setRateLimitingPeriodHours(1)). Hal ini mencegah aplikasi merekam lebih dari satu profil peluncuran per jam.
  4. Daftarkan pemroses: Memanggil registerForAllProfilingResults untuk menentukan callback yang menangani hasil. Callback ini menerima jalur profil yang disimpan melalui getResultFilePath().
  5. Tambahkan pemicu: Mendaftarkan daftar pemicu dengan ProfilingManager menggunakan addProfilingTriggers.
  6. Fire event: Memanggil reportFullyDrawn(), yang memancarkan peristiwa TRIGGER_TYPE_APP_FULLY_DRAWN ke sistem yang memicu pengumpulan profil dengan asumsi bahwa pelacakan latar belakang sistem sedang berjalan dan kuota pembatas laju tersedia. Langkah opsional ini menunjukkan alur end-to-end 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 ini:

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

Anda dapat menarik file menggunakan ADB. Misalnya, untuk menarik rekaman aktivitas sistem yang diambil dengan kode contoh menggunakan ADB, perintahnya 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 memvisualisasikan rekaman aktivitas ini, lihat Mengambil dan menganalisis data pembuatan profil.

Cara kerja penelusuran di latar belakang

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

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

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

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

Profil yang dipicu sistem memiliki ukuran maksimum yang ditentukan sistem, sehingga menggunakan buffer ring. Setelah buffer penuh, data rekaman aktivitas baru akan menggantikan data terlama. Seperti yang ditunjukkan pada Gambar 1, rekaman aktivitas yang diambil mungkin tidak mencakup seluruh durasi perekaman latar belakang jika buffer penuh; sebagai gantinya, rekaman aktivitas tersebut merepresentasikan aktivitas terbaru yang memicu perekaman.

Menerapkan pembatasan kapasitas khusus pemicu

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

ProfilingManager mendukung pembatasan kapasitas khusus pemicu yang ditentukan aplikasi. Hal ini memungkinkan Anda menambahkan lapisan lain pembatasan berbasis waktu selain pembatas kecepatan yang ada. Gunakan setRateLimitingPeriodHours API untuk menyetel waktu tunggu tertentu untuk pemicu. Setelah periode tunggu berakhir, Anda dapat memicunya lagi.

Men-debug pemicu secara lokal

Karena rekaman aktivitas latar belakang berjalan pada waktu acak, pemicuan proses debug secara lokal sulit dilakukan. Untuk memaksa rekaman aktivitas 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 rekaman aktivitas latar belakang berkelanjutan untuk paket yang ditentukan, sehingga setiap pemicu dapat mengumpulkan profil jika pembatas kecepatan mengizinkan.

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