Android 11 (API 級別 30) 以上版本支援快取應用程式凍結功能。這項功能會停止執行快取的程序,並減少可能在快取時嘗試運作的異常應用程式所造成的資源用量。
快取應用程式凍結器會將應用程式保留在 RAM 中,但不會佔用 CPU 資源。 如果 Android 判斷應用程式不應執行工作,但日後可能需要,就會凍結應用程式程序,而不是終止程序。這樣一來,當應用程式再次需要時,就不會發生冷啟動。
Android 會將快取應用程式的程序遷移至凍結的 cgroup,藉此凍結這些應用程式。這樣可減少有快取應用程式時的 CPU 耗用量 (無論是使用中或閒置)。您可以使用系統設定標記或開發人員選項,啟用應用程式凍結功能。
在 Android 14 (API 級別 34) 以上版本中,快取應用程式凍結器包含下列強大行為:
ActivityManagerService 會管理所有應用程式程序,並決定應用程式生命週期。CachedAppOptimizer 負責凍結應用程式程序。
應用程式程序凍結時,所有執行緒都會暫停,且在解除凍結前無法執行 CPU 工作。因此,應用程式無法執行垃圾收集 (GC),也無法回應記憶體修剪事件。詳情請參閱 ComponentCallbacks2.onTrimMemory(int)。為配合這項異動,Android 14 以上版本將有以下變更:
- 只要應用程式有可見的
Activity執行個體,系統就會在應用程式移至背景時立即通知TRIM_MEMORY_UI_HIDDEN。如果應用程式在生命週期中沒有使用者介面 (例如具有前景服務的應用程式),可能會收到TRIM_MEMORY_BACKGROUND。其他修剪事件不會傳送,因為應用程式符合這些事件的資格時,預期會凍結。 - 進入快取狀態後不久,系統可能會要求應用程式執行階段執行 GC,為可能凍結做準備。
- 應用程式程序凍結時,可能會發生額外的記憶體壓縮步驟,例如將骯髒頁面寫入備份儲存空間,以及將匿名頁面交換至 ZRAM。
- 如果特定應用程式的所有程序都凍結,系統會終止應用程式維護的所有有效 TCP 通訊端。這樣一來,通訊端的伺服器端就不會傳送 TCP 保持運作連線偵測 (ping),避免喚醒裝置數據機。
當快取應用程式程序的程序狀態從快取提升至重要性較高的狀態時,系統就會解除凍結這些程序。為減少 Android 14 以上版本中的解除凍結事件,當應用程式處於快取狀態時,系統會將註冊使用情境的廣播訊息排入佇列。註冊使用情境的廣播訊息是應用程式透過呼叫 Context.registerReceiver 動態註冊的接收器。系統只會在應用程式解除凍結後,傳送這些已加入佇列的廣播。相較之下,系統不會將資訊清單宣告的廣播排入佇列。資訊清單宣告的廣播訊息是使用 <receiver> 元素,在 AndroidManifest.xml 中靜態宣告的接收器。系統會立即解除凍結快取的應用程式,以便傳送資訊清單宣告的廣播訊息。
系統健康狀態影響
如果快取應用程式程序超過 MAX_CACHED_PROCESSES 個,Android 會終止近期最少使用的快取應用程式程序。在搭載 Android 14 以上版本的支援裝置上,MAX_CACHED_PROCESSES 大幅增加,因此裝置可在 RAM 中保留更多快取應用程式程序。
在 RAM 中快取更多應用程式,冷啟動次數最多可減少 30%,減少幅度會根據裝置 RAM 總量而有所不同。同時,系統會盡量減少快取應用程式的 CPU 耗用量,進而大幅節省電力。
冷凍櫃豁免
在特定情況下,應用程式程序可能會進入快取狀態,但仍保持未凍結狀態。這些豁免是實作詳細資料,日後可能會在 Android 版本中變更:
- 檔案鎖定:如果快取程序持有檔案鎖定,導致其他非快取程序遭到封鎖,持有鎖定的程序不會凍結。
BIND_WAIVE_PRIORITY繫結:使用Context.BIND_WAIVE_PRIORITY建立的繫結傳入應用程式程序後,這些程序可以進入快取狀態,但會保持未凍結狀態,直到所有連線的用戶端程序也快取為止。這項豁免適用於多程序應用程式,例如使用自訂分頁的網路瀏覽器。
實作應用程式凍結功能
快取應用程式凍結工具會運用核心 cgroup v2 凍結工具。如果裝置出廠時搭載相容的 Kernel,即可啟用這項功能。啟用「暫停執行已快取的應用程式」開發人員選項,或將裝置設定旗標 activity_manager_native_boot use_freezer 設為 true。例如:
adb shell device_config put activity_manager_native_boot use_freezer true && adb reboot將 use_freezer 旗標設為 false 或停用開發人員選項時,系統會停用凍結功能。例如:
adb shell device_config put activity_manager_native_boot use_freezer false && adb reboot您可以在軟體版本或更新中變更裝置設定,切換這項設定。
如要覆寫 MAX_CACHED_PROCESSES,例如將值設為 1024 以進行測試:
adb shell device_config put activity_manager max_cached_processes 1024
adb shell device_config set_sync_disabled_for_tests persistent如要還原 MAX_CACHED_PROCESSES 覆寫值,請按照下列步驟操作:
adb shell device_config delete activity_manager max_cached_processes
adb shell device_config set_sync_disabled_for_tests none應用程式凍結器不會公開官方 API,也沒有參考實作用戶端,但會使用隱藏的系統 API setProcessFrozen 凍結個別程序,並使用 enableFreezer 全域啟用或停用凍結功能。
處理自訂功能
應用程式程序在快取時不應執行任何工作,但部分應用程式可能具備自訂功能,這些功能由預期在快取時執行的程序提供支援。如果裝置上執行這類應用程式時啟用應用程式凍結功能,快取程序就會凍結,可能導致自訂功能無法運作。
如要解決這個問題,可以在程序需要執行任何工作前,將程序狀態變更為非快取。這項變更可讓應用程式保持運作。舉例來說,繫結的前景服務或前景狀態都屬於有效狀態。
常見故障模式
應用程式程序凍結時,不當的處理序間通訊 (IPC) 或工作排程可能會導致應用程式終止或發生非預期行為。
同步綁定資料傳輸至凍結的程序
如果用戶端應用程式程序將同步繫結器交易傳送至凍結的伺服器應用程式程序,系統會立即終止伺服器應用程式程序。這樣可避免用戶端執行緒在等待凍結伺服器的回應時無限期封鎖。用戶端執行緒隨後會收到 RemoteException,並觸發所有已註冊的事件監聽器。詳情請參閱 IBinder.linkToDeath。
根本原因:這類失敗通常是由用戶端應用程式中的錯誤所導致。當用戶端繫結至服務時,伺服器程序會繫結至用戶端,且在用戶端繫結前無法進入快取狀態。詳情請參閱 Context.bindService。不過,一旦用戶端呼叫 Context.unbindService,伺服器程序就會遭到快取並凍結。如果用戶端在取消繫結後繼續使用快取IBinder參照,可能會與凍結的程序通訊。
為避免發生這個問題,請確保用戶端應用程式在呼叫 Context.unbindService 後,立即捨棄 IBinder 參照。
非同步繫結器交易緩衝區溢位
當伺服器應用程式程序在凍結期間收到非同步 (oneway) 繫結器交易時,交易會緩衝到每個程序的緩衝區中。如果伺服器在凍結期間收到太多非同步交易,緩衝區就會溢位,系統也會終止伺服器應用程式程序。
為避免發生緩衝區溢位,請勿將過多的非同步繫結器交易傳送至可能已快取或凍結的程序。
解除凍結後重複執行排定的工作
如果應用程式執行重複性工作,這些工作會在程序凍結時暫停。詳情請參閱 ScheduledThreadPoolExecutor.scheduleAtFixedRate 或 Timer.scheduleAtFixedRate。當程序解除凍結時,累積的錯過執行作業可能會快速連續執行,幾乎不會延遲。
為避免應用程式解除凍結時執行次數暴增,請對背景工作使用 scheduleWithFixedDelay,而不是 scheduleAtFixedRate。你也可以使用 WorkManager。
測試及排解應用程式凍結問題
如要確認應用程式凍結器是否正常運作,或排解凍結器相關問題,請使用下列診斷工具和指令:
活動管理員指令
您可以使用 adb shell am 指令,手動控制特定程序的凍結和壓縮作業:
強制凍結程序:
adb shell am freeze <process>強制解除程序凍結:
adb shell am unfreeze <process>強制對程序執行完整的記憶體壓縮:
adb shell am compact full <process>
檢查 Logcat
查看 logcat,瞭解每次程序移入或移出冷凍庫時,凍結和解除凍結的項目:
adb logcat | grep -i "\(freezing\|froze\)"從 UnfreezeReason 通訊協定緩衝區列舉中,輸出解除凍結原因記錄的列舉值。
Dumpsys 檢查
使用 dumpsys activity 查看凍結程序清單:
adb shell dumpsys activity | grep -A 20 "Apps frozen:"檢查 /sys/fs/cgroup/uid_0/cgroup.freeze 檔案是否存在。
ApplicationExitInfo
如要查詢先前程序終止的原因,請參閱 ActivityManager.getHistoricalProcessExitReasons。如果應用程式程序因凍結相關問題 (例如在凍結時收到同步繫結器交易) 而終止,則退出原因會設為 ApplicationExitInfo.REASON_FREEZER。
Perfetto 追蹤
系統會將與凍結相關的事件發送至 Perfetto 追蹤記錄中 system_server 程序的 Freezer 軌:
Freeze和Unfreeze切片表示程序何時變更狀態。updateAppFreezeStateLSP事件會顯示系統伺服器重新檢查程序屬性,以做出凍結或解除凍結決策的時間。
您可以直接在 Perfetto UI 中檢查這些事件,或使用 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 %");
在 PerfettoSQL 標準程式庫中,凍結事件也會匯總在 android_freezer_events 表格中。