Freezer aplikasi yang di-cache

Android 11 (level API 30) atau yang lebih tinggi mendukung pembekuan aplikasi yang di-cache. Fitur ini menghentikan eksekusi untuk proses yang di-cache dan mengurangi penggunaan resource oleh aplikasi yang berperilaku tidak semestinya yang mungkin mencoba beroperasi saat di-cache.

Pembekuan aplikasi dalam cache menyimpan aplikasi di RAM sambil menjaganya agar tidak menggunakan CPU. Jika Android menentukan bahwa aplikasi tidak boleh melakukan pekerjaan, tetapi mungkin diperlukan di masa mendatang, Android akan membekukan proses aplikasi, bukan menghentikannya. Tindakan ini mencegah cold start saat aplikasi diperlukan lagi.

Android membekukan aplikasi yang di-cache dengan memigrasikan prosesnya ke cgroup yang dibekukan. Hal ini mengurangi konsumsi CPU aktif dan tidak aktif saat ada aplikasi yang di-cache aktif. Anda dapat mengaktifkan pembekuan aplikasi menggunakan tanda konfigurasi sistem atau opsi developer.

Di Android 14 (level API 34) dan yang lebih tinggi, pembekuan aplikasi yang di-cache mencakup perilaku yang kuat berikut:

  • Proses aplikasi dalam status cache dibekukan 10 detik setelah memasuki status cache.
  • Sistem akan segera mencairkan proses aplikasi yang dibekukan selama peristiwa siklus proses. Peristiwa ini mencakup penerimaan intent, memulai layanan tugas, atau pengguna melanjutkan aktivitas.

ActivityManagerService mengelola semua proses aplikasi dan membuat keputusan siklus proses aplikasi. CachedAppOptimizer bertanggung jawab untuk menghentikan proses aplikasi.

Saat proses aplikasi dibekukan, semua thread-nya ditangguhkan dan tidak dapat melakukan pekerjaan CPU hingga tidak dibekukan. Akibatnya, aplikasi tidak dapat melakukan pengumpulan sampah (GC) dan tidak dapat merespons peristiwa pengurangan memori. Untuk mengetahui informasi selengkapnya, lihat ComponentCallbacks2.onTrimMemory(int). Untuk mengakomodasi hal ini, mulai di Android 14:

  • Aplikasi dengan instance Activity yang terlihat akan diberi tahu tentang TRIM_MEMORY_UI_HIDDEN segera setelah aplikasi berpindah ke latar belakang. Aplikasi yang tetap berada dalam siklus proses tanpa UI, seperti aplikasi dengan layanan latar depan, mungkin menerima TRIM_MEMORY_BACKGROUND. Peristiwa pemangkasan lainnya tidak dikirimkan, karena saat aplikasi memenuhi syarat untuk peristiwa tersebut, aplikasi diharapkan dibekukan.
  • Segera setelah memasuki status cache, sistem dapat meminta runtime aplikasi untuk melakukan GC sebagai persiapan untuk berpotensi dibekukan.
  • Saat proses aplikasi dibekukan, langkah-langkah pemadatan memori tambahan dapat terjadi, seperti menulis halaman kotor ke penyimpanan pendukung dan menukar halaman anonim ke ZRAM.
  • Jika semua proses untuk aplikasi tertentu dibekukan, sistem akan menghentikan semua soket TCP aktif yang dikelola oleh aplikasi. Hal ini mencegah sisi server soket mengirimkan ping keepalive TCP yang akan mengaktifkan modem perangkat.

Proses aplikasi yang di-cache akan di-unfreeze saat status prosesnya ditingkatkan dari di-cache ke status kepentingan yang lebih tinggi. Untuk mengurangi peristiwa pembekuan di Android 14 dan yang lebih tinggi, sistem mengantrekan siaran yang terdaftar dalam konteks saat aplikasi berada dalam status cache. Siaran yang terdaftar dalam konteks adalah penerima yang didaftarkan aplikasi secara dinamis dengan memanggil Context.registerReceiver. Sistem mengirimkan siaran yang diantrekan ini hanya setelah aplikasi tidak dibekukan. Sebaliknya, sistem tidak mengantrekan siaran yang dideklarasikan manifes. Siaran yang dideklarasikan manifes adalah penerima yang dideklarasikan secara statis di AndroidManifest.xml menggunakan elemen <receiver>. Sistem akan segera membuka aplikasi yang di-cache untuk mengirimkan siaran yang dideklarasikan manifes.

Dampak kesehatan sistem

Android akan menghentikan proses aplikasi yang di-cache yang paling lama tidak digunakan jika ada lebih dari MAX_CACHED_PROCESSES proses aplikasi yang di-cache. Pada perangkat yang didukung dan menjalankan Android 14 atau yang lebih baru, MAX_CACHED_PROCESSES meningkat secara signifikan, sehingga perangkat dapat mempertahankan lebih banyak proses aplikasi yang di-cache dalam RAM.

Dengan mempertahankan lebih banyak aplikasi yang di-cache di RAM, cold start dapat dikurangi hingga 30%, dengan pengurangan yang diskalakan berdasarkan total RAM perangkat. Pada saat yang sama, konsumsi CPU oleh aplikasi yang di-cache diminimalkan, sehingga menghemat baterai secara signifikan.

Pengecualian freezer

Dalam kondisi tertentu, proses aplikasi dapat memasuki status cache, tetapi tetap tidak dibekukan. Pengecualian ini adalah detail implementasi dan dapat berubah di versi Android mendatang:

  • Kunci file: Jika proses yang di-cache memegang kunci file yang memblokir proses lain yang tidak di-cache, proses yang memegang kunci tidak dibekukan.
  • Pengikatan BIND_WAIVE_PRIORITY: Proses aplikasi dengan pengikatan masuk yang dibuat menggunakan Context.BIND_WAIVE_PRIORITY dapat memasuki status yang di-cache, tetapi tetap tidak dibekukan hingga semua proses klien yang terhubung juga di-cache. Pengecualian ini mendukung aplikasi multi-proses, seperti browser web yang menggunakan Tab Kustom.

Menerapkan pembekuan aplikasi

Pembekuan aplikasi yang di-cache menggunakan pembekuan cgroup v2 kernel. Perangkat yang dikirim dengan kernel yang kompatibel dapat mengaktifkannya. Aktifkan opsi developer Tunda eksekusi untuk aplikasi yang di-cache atau tetapkan flag konfigurasi perangkat activity_manager_native_boot use_freezer ke true. Contoh:

adb shell device_config put activity_manager_native_boot use_freezer true && adb reboot

Pembekuan dinonaktifkan saat Anda menyetel flag use_freezer ke false atau menonaktifkan opsi developer. Contoh:

adb shell device_config put activity_manager_native_boot use_freezer false && adb reboot

Anda dapat mengaktifkan/menonaktifkan setelan ini dengan mengubah konfigurasi perangkat dalam rilis atau update software.

Untuk mengganti MAX_CACHED_PROCESSES, misalnya, untuk menetapkan nilai ke 1024 untuk pengujian:

adb shell device_config put activity_manager max_cached_processes 1024
adb shell device_config set_sync_disabled_for_tests persistent

Untuk mengembalikan penggantian MAX_CACHED_PROCESSES:

adb shell device_config delete activity_manager max_cached_processes
adb shell device_config set_sync_disabled_for_tests none

Mulai Android 16 (level API 36) dan yang lebih tinggi, Android menyediakan API publik resmi, seperti IBinder.FrozenStateChangeCallback dan IBinder.addFrozenStateChangeCallback, untuk mengamati kapan proses jarak jauh dibekukan atau tidak dibekukan. Komponen yang berinteraksi dengan aplikasi yang mungkin di-cache dapat menggunakan API ini untuk melacak status dibekukan proses jarak jauh.

Persyaratan perangkat dan kernel

Pengoptimal aplikasi yang di-cache memerlukan dukungan cgroup v2 kernel. Selain itu, notifikasi perubahan status pembekuan menggunakan IBinder.FrozenStateChangeCallback memerlukan dukungan driver binder kernel, yang merupakan standar di Android Common Kernels (ACK) dan Generic Kernel Images (GKI) mulai dari Android 14 (level API 34) dan yang lebih tinggi.

Anda dapat memverifikasi apakah perangkat mendukung kemampuan ini menggunakan perintah adb standar, sebagai berikut:

  • Periksa apakah pembekuan diaktifkan di perangkat (untuk build pengguna atau debug):

    adb shell device_config get activity_manager_native_boot use_freezer

    Atau verifikasi bahwa proses sedang dibekukan secara aktif oleh sistem:

    adb shell dumpsys activity | grep -A 20 "Apps frozen:"
  • Periksa dukungan pengontrol pembekuan cgroup v2 (untuk perangkat apa pun):

    Pastikan freezer tercantum di antara pengontrol cgroup v2 yang tersedia:

    adb shell cat /sys/fs/cgroup/cgroup.controllers

    Atau, pada perangkat yang telah di-root atau build userdebug, verifikasi bahwa node freezer cgroup v2 dipasang di cgroup turunan:

    adb root && adb shell ls /sys/fs/cgroup/uid_0/cgroup.freeze

    Jika file ini ada, kernel mendukung pembekuan cgroup v2.

  • Periksa dukungan notifikasi perubahan status pembekuan (untuk perangkat apa pun):

    Di perangkat yang menjalankan Android 14 dan yang lebih tinggi dengan driver binder Generic Kernel Image (GKI) yang kompatibel, IBinder.addFrozenStateChangeCallback berhasil mendaftarkan callback. Jika driver binder kernel yang mendasarinya tidak mendukung notifikasi pembekuan, metode akan menampilkan UnsupportedOperationException.

Menangani fitur kustom

Proses aplikasi tidak diharapkan melakukan pekerjaan apa pun saat di-cache, tetapi beberapa aplikasi mungkin memiliki fitur kustom yang didukung oleh proses yang diharapkan berjalan saat di-cache. Jika pembekuan aplikasi diaktifkan pada perangkat yang menjalankan aplikasi tersebut, proses yang di-cache akan dibekukan dan dapat mencegah fitur kustom berfungsi.

Sebagai solusi sementara, Anda dapat mengubah status proses menjadi non-cache sebelum proses perlu melakukan pekerjaan apa pun. Perubahan ini memungkinkan aplikasi tetap aktif. Contoh status aktif mencakup layanan latar depan terikat atau status latar depan.

Mode kegagalan umum

Saat proses aplikasi dibekukan, komunikasi antar-proses (IPC) atau penjadwalan tugas yang tidak tepat dapat menyebabkan penghentian aplikasi atau perilaku yang tidak terduga.

Transaksi binder sinkron ke proses yang dibekukan

Saat proses aplikasi klien mengirim transaksi binder sinkron ke proses aplikasi server yang dibekukan, sistem akan segera menghentikan proses aplikasi server. Hal ini mencegah thread klien diblokir tanpa batas waktu saat menunggu respons dari server yang dibekukan. Kemudian, thread klien menerima RemoteException, dan setiap pemroses yang terdaftar akan dipicu. Untuk mengetahui informasi selengkapnya, lihat IBinder.linkToDeath.

Penyebab utama: Kegagalan ini biasanya disebabkan oleh bug di aplikasi klien. Saat klien terikat ke layanan, proses server terikat ke klien dan dicegah memasuki status yang di-cache sebelum klien melakukannya. Untuk mengetahui informasi selengkapnya, lihat Context.bindService. Namun, setelah klien memanggil Context.unbindService, proses server dapat di-cache dan dibekukan. Jika klien terus menggunakan referensi IBinder yang di-cache setelah membatalkan pengikatan, klien berisiko berkomunikasi dengan proses yang dibekukan.

Untuk menghindari masalah ini, pastikan aplikasi klien menghapus referensi IBinder segera setelah memanggil Context.unbindService.

Mengelola panggilan balik jarak jauh ke proses klien

Layanan dan komponen sistem yang mempertahankan callback binder yang berjalan lama ke proses klien dapat mencegah kegagalan sinkron dan overflow buffer asinkron dengan melacak status klien yang dibekukan:

  • Mendaftarkan pemroses perubahan status: Gunakan IBinder.addFrozenStateChangeCallback pada token binder klien yang masuk untuk menerima notifikasi saat proses klien masuk atau keluar dari pembekuan.
  • Menjeda pengiriman saat dibekukan: Saat klien memasuki STATE_FROZEN, jeda pengiriman callback atau pembaruan status ke klien tersebut.
  • Lanjutkan dan kirim setelah pencairan: Saat klien bertransisi ke STATE_UNFROZEN, lanjutkan pengiriman callback dan kirim pembaruan yang diperlukan yang dikelompokkan atau digabungkan.
  • Menggunakan RemoteCallbackList: Layanan sistem yang menggunakan RemoteCallbackList dapat mengonfigurasi kebijakan tujuan panggilan yang dibekukan untuk menjeda dan melanjutkan pengiriman callback secara otomatis tanpa mempertahankan logika pelacakan manual. Untuk mengetahui informasi selengkapnya, lihat Rekomendasi pembekuan Binder untuk layanan sistem.

Overflow buffer transaksi binder asinkron

Saat proses aplikasi server menerima transaksi binder asinkron (oneway) saat dibekukan, transaksi akan di-buffer dalam buffer per proses. Jika server menerima terlalu banyak transaksi asinkron saat dibekukan, buffer akan meluap, dan sistem akan menghentikan proses aplikasi server.

Untuk mencegah buffer overflow ini, hindari mengirim transaksi binder asinkron yang berlebihan ke proses yang mungkin di-cache atau dibekukan.

Eksekusi berulang tugas terjadwal setelah tidak dibekukan

Jika aplikasi menjalankan tugas berulang, tugas tersebut akan ditangguhkan saat proses dibekukan. Untuk mengetahui informasi selengkapnya, lihat ScheduledThreadPoolExecutor.scheduleAtFixedRate atau Timer.scheduleAtFixedRate. Saat proses dilanjutkan, eksekusi yang terlewatkan yang terakumulasi dapat berjalan dengan cepat secara berurutan tanpa penundaan.

Untuk mencegah lonjakan eksekusi saat aplikasi tidak dibekukan, gunakan scheduleWithFixedDelay, bukan scheduleAtFixedRate untuk tugas latar belakang. Anda juga dapat menggunakan WorkManager.

Menguji dan memecahkan masalah pembekuan aplikasi

Untuk memverifikasi bahwa pembekuan aplikasi berfungsi sebagaimana mestinya atau untuk memecahkan masalah terkait pembekuan, gunakan alat dan perintah diagnostik berikut:

Perintah pengelola aktivitas

Anda dapat menggunakan perintah adb shell am untuk mengontrol pembekuan dan pemadatan secara manual untuk proses tertentu:

  • Memaksa proses berhenti berfungsi:

    adb shell am freeze <process>
  • Memaksa proses untuk berhenti dibekukan:

    adb shell am unfreeze <process>
  • Memaksa pemadatan memori penuh pada suatu proses:

    adb shell am compact full <process>

Pemeriksaan Logcat

Lihat logcat untuk melihat entri yang dibekukan dan tidak dibekukan setiap kali proses bermigrasi ke dalam atau keluar dari pembeku:

adb logcat | grep -i "\(freezing\|froze\)"

Output log alasan pembatalan pembekuan mencantumkan nilai dari enum buffer protokol UnfreezeReason.

Pemeriksaan dumpsys

Periksa daftar proses yang dibekukan menggunakan dumpsys activity:

adb shell dumpsys activity | grep -A 20 "Apps frozen:"

Periksa keberadaan file /sys/fs/cgroup/uid_0/cgroup.freeze.

ApplicationExitInfo

Untuk membuat kueri alasan penghentian proses sebelumnya, lihat ActivityManager.getHistoricalProcessExitReasons. Jika proses aplikasi dihentikan karena masalah terkait pembekuan, seperti menerima transaksi binder sinkron saat dibekukan, alasan keluar ditetapkan ke ApplicationExitInfo.REASON_FREEZER.

Pelacakan Perfetto

Peristiwa terkait Freezer dipancarkan ke jalur bernama Freezer di bawah proses system_server dalam rekaman aktivitas Perfetto:

  • Slice Freeze dan Unfreeze menunjukkan kapan status proses berubah.
  • Peristiwa updateAppFreezeStateLSP menunjukkan kapan server sistem memeriksa ulang atribut proses untuk membuat keputusan pembekuan atau pembatalan pembekuan.

Anda dapat memeriksa peristiwa ini secara langsung di UI Perfetto atau menganalisisnya menggunakan PerfettoSQL:

INCLUDE PERFETTO MODULE slices.with_context;
SELECT *
FROM process_slice
WHERE process_name = "system_server"
AND track_name = "Freezer"
AND (name LIKE "Freeze %" OR name LIKE "Unfreeze %");

Di library standar PerfettoSQL, peristiwa pembekuan juga diringkas dalam tabel android_freezer_events.