Konfigurasi Layanan ART

Sebelum memulai, lihat ringkasan umum Layanan ART.

Mulai Android 14, kompilasi AOT di perangkat untuk aplikasi (alias dexopt) ditangani oleh Layanan ART. Layanan ART adalah bagian dari modul ART, dan Anda dapat menyesuaikannya melalui properti sistem dan API.

Properti sistem

Layanan ART mendukung semua opsi dex2oat yang relevan.

Selain itu, Layanan ART mendukung properti sistem berikut:

pm.dexopt.<reason>

Ini adalah kumpulan properti sistem yang menentukan filter compiler default untuk semua alasan kompilasi standar yang dijelaskan dalam skenario Dexopt.

Untuk informasi selengkapnya, lihat Filter compiler.

Nilai default standarnya adalah:

pm.dexopt.first-boot=verify
pm.dexopt.boot-after-ota=verify
pm.dexopt.boot-after-mainline-update=verify
pm.dexopt.bg-dexopt=speed-profile
pm.dexopt.inactive=verify
pm.dexopt.cmdline=verify

pm.dexopt.shared (default: kecepatan)

Ini adalah filter compiler penggantian untuk aplikasi yang digunakan oleh aplikasi lain.

Pada prinsipnya, Layanan ART melakukan kompilasi terpandu profil (speed-profile) untuk semua aplikasi jika memungkinkan, biasanya selama dexopt latar belakang. Namun, ada beberapa aplikasi yang digunakan oleh aplikasi lain (baik melalui <uses-library> maupun dimuat secara dinamis menggunakan Context#createPackageContext dengan CONTEXT_INCLUDE_CODE). Aplikasi tersebut tidak dapat menggunakan profil lokal karena alasan privasi.

Untuk aplikasi tersebut, jika kompilasi terpandu profil diminta, Layanan ART akan mencoba menggunakan profil cloud terlebih dahulu. Jika profil cloud tidak ada, Layanan ART akan kembali menggunakan filter compiler yang ditentukan oleh pm.dexopt.shared.

Jika kompilasi yang diminta tidak dipandu oleh profil, properti ini tidak akan berpengaruh.

pm.dexopt.<reason>.concurrency (default: 1)

Ini adalah jumlah pemanggilan dex2oat untuk alasan kompilasi yang telah ditentukan sebelumnya (first-boot, boot-after-ota, boot-after-mainline-update, dan bg-dexopt).

Perhatikan bahwa efek opsi ini digabungkan dengan opsi penggunaan resource dex2oat (dalvik.vm.*dex2oat-threads, dalvik.vm.*dex2oat-cpu-set, dan profil tugas):

  • dalvik.vm.*dex2oat-threads mengontrol jumlah thread untuk setiap pemanggilan dex2oat, sedangkan pm.dexopt.<reason>.concurrency mengontrol jumlah pemanggilan dex2oat. Artinya, jumlah maksimum thread serentak adalah hasil dari dua properti sistem.
  • dalvik.vm.*dex2oat-cpu-set dan profil tugas selalu mengikat penggunaan core CPU, terlepas dari jumlah maksimum thread serentak (dibahas di atas).

Pemanggilan dex2oat tunggal mungkin tidak sepenuhnya menggunakan semua core CPU, terlepas dari dalvik.vm.*dex2oat-threads. Oleh karena itu, meningkatkan jumlah pemanggilan dex2oat (pm.dexopt.<reason>.concurrency) dapat memanfaatkan core CPU dengan lebih baik, untuk mempercepat progres dexopt secara keseluruhan. Hal ini sangat berguna selama proses booting.

Namun, terlalu banyak pemanggilan dex2oat dapat menyebabkan perangkat kehabisan memori, meskipun hal ini dapat dimitigasi dengan menetapkan dalvik.vm.dex2oat-swap ke true untuk memungkinkan penggunaan file swap. Terlalu banyak pemanggilan juga dapat menyebabkan peralihan konteks yang tidak perlu. Oleh karena itu, angka ini harus disesuaikan dengan cermat berdasarkan produk.

pm.dexopt.downgrade_after_inactive_days (default: tidak ditetapkan)

Jika opsi ini ditetapkan, Layanan ART hanya akan melakukan dexopt pada aplikasi yang digunakan dalam jumlah hari terakhir yang ditentukan.

Selain itu, jika penyimpanan hampir habis, selama dexopt latar belakang, Layanan ART akan mendowngrade filter compiler aplikasi yang tidak digunakan dalam jumlah hari terakhir yang ditentukan, untuk mengosongkan ruang. Alasan compiler untuk hal ini adalah inactive, dan filter compiler ditentukan oleh pm.dexopt.inactive. Batas ruang untuk memicu fitur ini adalah batas ruang rendah Pengelola Penyimpanan (dapat dikonfigurasi melalui setelan global sys_storage_threshold_percentage dan sys_storage_threshold_max_bytes, default: 500 MB) ditambah 500 MB.

Jika Anda menyesuaikan daftar paket melalui ArtManagerLocal#setBatchDexoptStartCallback, paket dalam daftar yang disediakan oleh BatchDexoptStartCallback untuk bg-dexopt tidak akan pernah didowngrade.

pm.dexopt.disable_bg_dexopt (default: false)

Ini hanya untuk pengujian. Hal ini mencegah Layanan ART menjadwalkan tugas dexopt latar belakang.

Jika tugas dexopt latar belakang sudah dijadwalkan, tetapi belum berjalan, opsi ini tidak akan berpengaruh. Artinya, tugas akan tetap berjalan.

Urutan perintah yang direkomendasikan untuk mencegah tugas dexopt latar belakang berjalan adalah:

setprop pm.dexopt.disable_bg_dexopt true
pm bg-dexopt-job --disable

Baris pertama mencegah tugas dexopt latar belakang dijadwalkan jika belum dijadwalkan. Baris kedua membatalkan penjadwalan tugas dexopt latar belakang, jika sudah dijadwalkan, dan segera membatalkan tugas dexopt latar belakang, jika sedang berjalan.

ART Service API

Layanan ART mengekspos Java API untuk penyesuaian. API ditentukan di ArtManagerLocal. Lihat Javadoc di art/libartservice/service/java/com/android/server/art/ArtManagerLocal.java untuk penggunaan (sumber Android 14, sumber pengembangan yang belum dirilis).

ArtManagerLocal adalah singleton yang disimpan oleh LocalManagerRegistry. Fungsi helper com.android.server.pm.DexOptHelper#getArtManagerLocal membantu Anda mendapatkannya.

import static com.android.server.pm.DexOptHelper.getArtManagerLocal;

Sebagian besar API memerlukan instance PackageManagerLocal.FilteredSnapshot, yang menyimpan informasi semua aplikasi. Anda bisa mendapatkannya dengan memanggil PackageManagerLocal#withFilteredSnapshot, dengan PackageManagerLocal juga merupakan singleton yang disimpan oleh LocalManagerRegistry dan dapat diperoleh dari com.android.server.pm.PackageManagerServiceUtils#getPackageManagerLocal.

import static com.android.server.pm.PackageManagerServiceUtils.getPackageManagerLocal;

Berikut adalah beberapa kasus penggunaan umum API.

Memicu dexopt untuk aplikasi

Anda dapat memicu dexopt untuk aplikasi apa pun kapan saja dengan memanggil ArtManagerLocal#dexoptPackage.

try (var snapshot = getPackageManagerLocal().withFilteredSnapshot()) {
  getArtManagerLocal().dexoptPackage(
      snapshot,
      "com.google.android.calculator",
      new DexoptParams.Builder(ReasonMapping.REASON_INSTALL).build());
}

Anda juga dapat meneruskan alasan dexopt Anda sendiri. Jika Anda melakukannya, class prioritas dan filter compiler harus ditetapkan secara eksplisit.

try (var snapshot = getPackageManagerLocal().withFilteredSnapshot()) {
  getArtManagerLocal().dexoptPackage(
      snapshot,
      "com.google.android.calculator",
      new DexoptParams.Builder("my-reason")
          .setCompilerFilter("speed-profile")
          .setPriorityClass(ArtFlags.PRIORITY_BACKGROUND)
          .build());
}

Membatalkan dexopt

Jika operasi dimulai oleh panggilan dexoptPackage, Anda dapat meneruskan sinyal pembatalan, yang memungkinkan Anda membatalkan operasi pada waktu tertentu. Hal ini dapat berguna saat Anda menjalankan dexopt secara asinkron.

Executor executor = ...;  // Your asynchronous executor here.
var cancellationSignal = new CancellationSignal();
executor.execute(() -> {
  try (var snapshot = getPackageManagerLocal().withFilteredSnapshot()) {
    getArtManagerLocal().dexoptPackage(
        snapshot,
        "com.google.android.calculator",
        new DexoptParams.Builder(ReasonMapping.REASON_INSTALL).build(),
        cancellationSignal);
  }
});

// When you want to cancel the operation.
cancellationSignal.cancel();

Anda juga dapat membatalkan dexopt latar belakang, yang dimulai oleh Layanan ART.

getArtManagerLocal().cancelBackgroundDexoptJob();

Mendapatkan hasil dexopt

Jika operasi dimulai oleh panggilan dexoptPackage, Anda bisa mendapatkan hasilnya dari nilai yang ditampilkan.

DexoptResult result;
try (var snapshot = getPackageManagerLocal().withFilteredSnapshot()) {
  result = getArtManagerLocal().dexoptPackage(...);
}

// Process the result here.
...

Layanan ART juga memulai operasi dexopt itu sendiri dalam banyak skenario, seperti dexopt latar belakang. Untuk memproses semua hasil dexopt, baik operasi dimulai oleh panggilan dexoptPackage atau oleh Layanan ART, gunakan ArtManagerLocal#addDexoptDoneCallback.

getArtManagerLocal().addDexoptDoneCallback(
    false /* onlyIncludeUpdates */,
    Runnable::run,
    (result) -> {
      // Process the result here.
      ...
    });

Argumen pertama menentukan apakah hanya akan menyertakan update dalam hasil. Jika Anda hanya ingin memproses paket yang diupdate oleh dexopt, tetapkan nilai ini ke true.

Argumen kedua adalah eksekutor callback. Untuk menjalankan callback pada thread yang sama dengan yang melakukan dexopt, gunakan Runnable::run. Jika Anda tidak ingin callback memblokir dexopt, gunakan eksekutor asinkron.

Anda dapat menambahkan beberapa callback, dan Layanan ART akan menjalankan semuanya secara berurutan. Semua callback akan tetap aktif untuk semua panggilan mendatang, kecuali jika Anda menghapusnya.

Jika Anda ingin menghapus callback, simpan referensi callback saat Anda menambahkannya, dan gunakan ArtManagerLocal#removeDexoptDoneCallback.

DexoptDoneCallback callback = (result) -> {
  // Process the result here.
  ...
};

getArtManagerLocal().addDexoptDoneCallback(
    false /* onlyIncludeUpdates */, Runnable::run, callback);

// When you want to remove it.
getArtManagerLocal().removeDexoptDoneCallback(callback);

Menyesuaikan daftar paket dan parameter dexopt

Layanan ART memulai operasi dexopt sendiri selama dexopt booting dan latar belakang. Untuk menyesuaikan daftar paket atau parameter dexopt untuk operasi tersebut, gunakan ArtManagerLocal#setBatchDexoptStartCallback.

getArtManagerLocal().setBatchDexoptStartCallback(
    Runnable::run,
    (snapshot, reason, defaultPackages, builder, cancellationSignal) -> {
      switch (reason) {
        case ReasonMapping.REASON_BG_DEXOPT:
          var myPackages = new ArrayList<String>(defaultPackages);
          myPackages.add(...);
          myPackages.remove(...);
          myPackages.sort(...);
          builder.setPackages(myPackages);
          break;
        default:
          // Ignore unknown reasons.
      }
    });

Anda dapat menambahkan item ke daftar paket, menghapus item dari daftar, mengurutkannya, atau bahkan menggunakan daftar yang sama sekali berbeda.

Callback Anda harus mengabaikan alasan yang tidak diketahui karena lebih banyak alasan dapat ditambahkan di masa mendatang.

Anda dapat menetapkan maksimal satu BatchDexoptStartCallback. Callback akan tetap aktif untuk semua panggilan mendatang kecuali jika Anda menghapusnya.

Jika Anda ingin menghapus callback, gunakan ArtManagerLocal#clearBatchDexoptStartCallback.

getArtManagerLocal().clearBatchDexoptStartCallback();

Menyesuaikan parameter tugas dexopt latar belakang

Secara default, tugas dexopt latar belakang berjalan sekali sehari saat perangkat tidak ada aktivitas dan sedang diisi dayanya. Hal ini dapat diubah menggunakan ArtManagerLocal#setScheduleBackgroundDexoptJobCallback.

getArtManagerLocal().setScheduleBackgroundDexoptJobCallback(
    Runnable::run,
    builder -> {
      builder.setPeriodic(TimeUnit.DAYS.toMillis(2));
    });

Anda dapat menetapkan maksimal satu ScheduleBackgroundDexoptJobCallback. Callback akan tetap aktif untuk semua panggilan mendatang kecuali jika Anda menghapusnya.

Jika Anda ingin menghapus callback, gunakan ArtManagerLocal#clearScheduleBackgroundDexoptJobCallback.

getArtManagerLocal().clearScheduleBackgroundDexoptJobCallback();

Menonaktifkan dexopt untuk sementara

Setiap operasi dexopt yang dimulai oleh Layanan ART akan memicu BatchDexoptStartCallback. Anda dapat terus membatalkan operasi untuk menonaktifkan dexopt secara efektif.

Jika operasi yang Anda batalkan adalah dexopt latar belakang, operasi tersebut akan mengikuti kebijakan percobaan ulang default (30 detik, eksponensial, dibatasi hingga 5 jam).

// Good example.

var shouldDisableDexopt = new AtomicBoolean(false);

getArtManagerLocal().setBatchDexoptStartCallback(
    Runnable::run,
    (snapshot, reason, defaultPackages, builder, cancellationSignal) -> {
      if (shouldDisableDexopt.get()) {
        cancellationSignal.cancel();
      }
    });

// Disable dexopt.
shouldDisableDexopt.set(true);
getArtManagerLocal().cancelBackgroundDexoptJob();

// Re-enable dexopt.
shouldDisableDexopt.set(false);

Anda dapat memiliki maksimal satu BatchDexoptStartCallback. Jika Anda juga ingin menggunakan BatchDexoptStartCallback untuk menyesuaikan daftar paket atau parameter dexopt, Anda harus menggabungkan kode menjadi satu callback.

// Bad example.

// Disable dexopt.
getArtManagerLocal().unscheduleBackgroundDexoptJob();

// Re-enable dexopt.
getArtManagerLocal().scheduleBackgroundDexoptJob();

Operasi dexopt yang dilakukan pada penginstalan aplikasi tidak dimulai oleh Layanan ART. Sebagai gantinya, proses ini dimulai oleh pengelola paket melalui panggilan dexoptPackage. Oleh karena itu, tindakan ini tidak memicu BatchDexoptStartCallback. Untuk menonaktifkan dexopt saat penginstalan aplikasi, cegah pengelola paket memanggil dexoptPackage.

Mengganti filter compiler untuk paket tertentu (Android 15+)

Anda dapat mengganti filter compiler untuk paket tertentu dengan mendaftarkan callback melalui setAdjustCompilerFilterCallback. Callback dipanggil setiap kali paket akan dide-dexopt, terlepas dari apakah dexopt dimulai oleh Layanan ART selama booting dan dexopt latar belakang atau oleh panggilan API dexoptPackage.

Jika paket tidak memerlukan penyesuaian, callback harus menampilkan originalCompilerFilter.

getArtManagerLocal().setAdjustCompilerFilterCallback(
    Runnable::run,
    (packageName, originalCompilerFilter, reason) -> {
      if (isVeryImportantPackage(packageName)) {
        return "speed-profile";
      }
      return originalCompilerFilter;
    });

Anda hanya dapat menetapkan satu AdjustCompilerFilterCallback. Jika ingin menggunakan AdjustCompilerFilterCallback guna mengganti filter compiler untuk beberapa paket, Anda harus menggabungkan kode menjadi satu callback. Callback tetap aktif untuk semua panggilan mendatang, kecuali jika Anda menghapusnya.

Jika Anda ingin menghapus callback, gunakan ArtManagerLocal#clearAdjustCompilerFilterCallback.

getArtManagerLocal().clearAdjustCompilerFilterCallback();

Penyesuaian lainnya

Layanan ART juga mendukung beberapa penyesuaian lainnya.

Menetapkan nilai minimum termal untuk dexopt latar belakang

Kontrol termal tugas dexopt latar belakang dilakukan oleh Job Scheduler. Tugas akan segera dibatalkan saat suhu mencapai THERMAL_STATUS_MODERATE. Batas minimum THERMAL_STATUS_MODERATE dapat disesuaikan.

Menentukan apakah dexopt latar belakang berjalan

Tugas dexopt latar belakang dikelola oleh Job Scheduler, dan ID tugasnya adalah 27873780. Untuk menentukan apakah tugas sedang berjalan, gunakan Job Scheduler API.

// Good example.

var jobScheduler =
    Objects.requireNonNull(mContext.getSystemService(JobScheduler.class));
int reason = jobScheduler.getPendingJobReason(27873780);

if (reason == PENDING_JOB_REASON_EXECUTING) {
  // Do something when the job is running.
  ...
}
// Bad example.

var backgroundDexoptRunning = new AtomicBoolean(false);

getArtManagerLocal().setBatchDexoptStartCallback(
    Runnable::run,
    (snapshot, reason, defaultPackages, builder, cancellationSignal) -> {
      if (reason.equals(ReasonMapping.REASON_BG_DEXOPT)) {
        backgroundDexoptRunning.set(true);
      }
    });

getArtManagerLocal().addDexoptDoneCallback(
    false /* onlyIncludeUpdates */,
    Runnable::run,
    (result) -> {
      if (result.getReason().equals(ReasonMapping.REASON_BG_DEXOPT)) {
        backgroundDexoptRunning.set(false);
      }
    });

if (backgroundDexoptRunning.get()) {
  // Do something when the job is running.
  ...
}

Memberikan profil untuk dexopt

Untuk menggunakan profil guna memandu dexopt, tempatkan file .prof atau file .dm di samping APK.

File .prof harus berupa file profil berformat biner, dan nama file harus nama file APK + .prof. Misalnya,

base.apk.prof

Nama file file .dm harus berupa nama file APK dengan ekstensi diganti dengan .dm. Misalnya,

base.dm

Untuk memverifikasi bahwa profil digunakan untuk dexopt, jalankan dexopt dengan speed-profile dan periksa hasilnya.

pm art clear-app-profiles <package-name>
pm compile -m speed-profile -f -v <package-name>

Baris pertama akan menghapus semua profil yang dihasilkan oleh runtime (yaitu, profil dalam /data/misc/profiles), jika ada, untuk memastikan bahwa profil di samping APK adalah satu-satunya profil yang mungkin dapat digunakan Layanan ART. Baris kedua menjalankan dexopt dengan speed-profile, dan meneruskan -v untuk mencetak hasil panjang.

Jika profil sedang digunakan, Anda akan melihat actualCompilerFilter=speed-profile dalam hasilnya. Jika tidak, Anda akan melihat actualCompilerFilter=verify. Misalnya,

DexContainerFileDexoptResult{dexContainerFile=/data/app/~~QR0fTV0UbDbIP1Su7XzyPg==/com.google.android.gms-LvusF2uARKOtBbcaPHdUtQ==/base.apk, primaryAbi=true, abi=x86_64, actualCompilerFilter=speed-profile, status=PERFORMED, dex2oatWallTimeMillis=4549, dex2oatCpuTimeMillis=14550, sizeBytes=3715344, sizeBeforeBytes=3715344}

Alasan umum mengapa Layanan ART tidak menggunakan profil meliputi hal-hal berikut:

  • Profil memiliki nama file yang salah atau tidak berada di samping APK.
  • Format profil salah.
  • Profil tidak cocok dengan APK. (Checksum dalam profil tidak cocok dengan checksum file .dex dalam APK.)