Freezer aplikasi yang di-cache

Android 11 (level API 30) atau yang lebih tinggi mendukung freezer 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.

Freezer aplikasi yang di-cache menyimpan aplikasi di RAM sekaligus 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. Hal 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 jika ada aplikasi yang di-cache aktif. Anda dapat mengaktifkan freezer aplikasi menggunakan flag konfigurasi sistem atau opsi developer.

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

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

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

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

  • Aplikasi dengan instance Activity yang terlihat akan diberi tahu tentang TRIM_MEMORY_UI_HIDDEN segera setelah 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 mungkin meminta runtime aplikasi untuk melakukan GC sebagai persiapan agar berpotensi dibekukan.
  • Saat proses aplikasi dibekukan, langkah-langkah pemadatan memori tambahan mungkin 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 mengirim ping TCP keepalive yang akan mengaktifkan modem perangkat.

Proses aplikasi yang di-cache tidak dibekukan saat status prosesnya ditingkatkan dari cache ke status prioritas yang lebih tinggi. Untuk mengurangi peristiwa tidak dibekukan di Android 14 dan yang lebih tinggi, sistem akan 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 hanya mengirimkan siaran yang diantrekan ini 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 mengaktifkan kembali aplikasi yang di-cache untuk mengirimkan siaran yang dideklarasikan manifes.

Dampak kesehatan sistem

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

Mempertahankan lebih banyak aplikasi yang di-cache di RAM menghasilkan pengurangan cold start 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 mungkin memasuki status cache tetapi tetap tidak dibekukan. Pengecualian ini adalah detail implementasi dan mungkin berubah pada versi Android mendatang:

  • Kunci file: Jika proses yang di-cache memiliki kunci file yang memblokir proses non-cache lainnya, proses yang memiliki kunci tidak akan dibekukan.
  • BIND_WAIVE_PRIORITY binding: Proses aplikasi dengan binding masuk yang dibuat menggunakan Context.BIND_WAIVE_PRIORITY dapat memasuki status 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.

Mengimplementasikan freezer aplikasi

Freezer aplikasi yang di-cache memanfaatkan freezer cgroup v2 kernel. Perangkat yang dikirimkan dengan kernel yang kompatibel dapat mengaktifkannya. Aktifkan opsi developer Suspend execution for cached apps 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

Freezer dinonaktifkan saat Anda menetapkan 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 mengalihkan 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

Freezer aplikasi tidak mengekspos API resmi dan tidak memiliki klien implementasi referensi, tetapi menggunakan API sistem tersembunyi setProcessFrozen untuk membekukan proses individual dan enableFreezer untuk mengaktifkan atau menonaktifkan pembekuan secara global.

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. Saat freezer aplikasi diaktifkan di perangkat yang menjalankan aplikasi tersebut, proses yang di-cache akan dibekukan dan mungkin mencegah fitur kustom berfungsi.

Sebagai solusinya, 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. Thread klien kemudian menerima RemoteException, dan semua pemroses terdaftar akan dipicu. Untuk mengetahui detailnya, 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 cache sebelum klien melakukannya. Untuk mengetahui detailnya, lihat Context.bindService. Namun, setelah klien memanggil Context.unbindService, proses server dapat menjadi di-cache dan dibekukan. Jika klien terus menggunakan referensi yang di-cache IBinder setelah tidak terikat, klien berisiko berkomunikasi dengan proses yang dibekukan.

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

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 overflow buffer ini, hindari mengirim transaksi binder asinkron yang berlebihan ke proses yang mungkin di-cache atau dibekukan.

Eksekusi berulang tugas terjadwal saat tidak dibekukan

Jika aplikasi menjalankan tugas berulang, tugas tersebut akan ditangguhkan saat proses dibekukan. Untuk mengetahui detailnya, lihat ScheduledThreadPoolExecutor.scheduleAtFixedRate atau Timer.scheduleAtFixedRate. Saat proses tidak dibekukan, eksekusi yang terlewat dan 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 freezer aplikasi

Untuk memverifikasi bahwa freezer aplikasi berfungsi sebagaimana mestinya atau untuk memecahkan masalah terkait freezer, 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 untuk dibekukan:

    adb shell am freeze <process>
  • Memaksa proses untuk tidak 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 masuk atau keluar dari freezer:

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

Log alasan tidak dibekukan menampilkan nilai yang dihitung 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 mengkueri alasan penghentian proses sebelumnya, lihat ActivityManager.getHistoricalProcessExitReasons. Jika proses aplikasi dihentikan karena masalah terkait freezer, seperti menerima transaksi binder sinkron saat dibekukan, alasan keluar akan ditetapkan ke ApplicationExitInfo.REASON_FREEZER.

Pelacakan Perfetto

Peristiwa terkait freezer akan ditampilkan ke trek bernama Freezer di bawah proses system_server dalam rekaman aktivitas Perfetto:

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

Anda dapat memeriksa peristiwa ini 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 freezer juga diringkas dalam tabel android_freezer_events.