Android 11 (API 級別 30) 以上版本支援快取應用程式凍結功能。這項功能會停止執行快取程序,並減少可能在快取時嘗試運作的異常應用程式所造成的資源用量。
快取應用程式凍結器會將應用程式保留在 RAM 中,同時讓應用程式遠離 CPU。 如果 Android 判斷應用程式不應執行工作,但日後可能需要,就會凍結應用程式程序,而不是終止程序。這樣一來,應用程式再次需要時,就不會發生冷啟動。
Android 會將快取應用程式的程序遷移至凍結的 cgroup,藉此凍結這些應用程式。這樣可減少有作用中快取應用程式時,CPU 的作用中和閒置耗用量。您可以使用系統設定旗標或開發人員選項啟用應用程式凍結功能。
在 Android 14 (API 級別 34) 以上版本中,快取應用程式凍結器包含下列強大行為:
- 應用程式處理程序進入快取狀態後,會在 10 秒內凍結。
- 在生命週期事件期間,系統會立即解除凍結凍結的應用程式程序。這些事件包括接收 intent、啟動 job service,或是使用者繼續執行 activity。
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 1024adb shell device_config set_sync_disabled_for_tests persistent
如要還原 MAX_CACHED_PROCESSES 覆寫值,請按照下列步驟操作:
adb shell device_config delete activity_manager max_cached_processesadb shell device_config set_sync_disabled_for_tests none
在 Android 16 (API 級別 36) 以上版本中,Android 提供官方公開 API,例如 IBinder.FrozenStateChangeCallback 和 IBinder.addFrozenStateChangeCallback,可觀察遠端程序凍結或解除凍結的時間。與可能遭到快取的應用程式互動的元件,可以使用這些 API 追蹤遠端程序的凍結狀態。
裝置和核心需求
快取應用程式凍結器需要核心 cgroup v2 支援。此外,使用 IBinder.FrozenStateChangeCallback 凍結狀態變更通知需要核心繫結器驅動程式支援,這項支援是 Android 14 (API 級別 34) 以上版本中 Android Common Kernels (ACK) 和 Generic Kernel Images (GKI) 的標準配備。
如要確認裝置是否支援這些功能,請使用標準 adb 指令,如下所示:
檢查裝置是否已啟用凍結功能 (適用於使用者或偵錯版本):
adb shell device_config get activity_manager_native_boot use_freezer或者,確認系統是否主動凍結程序:
adb shell dumpsys activity | grep -A 20 "Apps frozen:"檢查 cgroup v2 凍結控制器支援 (適用於任何裝置):
確認
freezer列在可用的 cgroup v2 控制器中:adb shell cat /sys/fs/cgroup/cgroup.controllers或者,在已解鎖裝置或 userdebug 建構版本上,確認 cgroup v2 freezer 節點已掛接至子 cgroup:
adb root && adb shell ls /sys/fs/cgroup/uid_0/cgroup.freeze如果這個檔案存在,表示核心支援 cgroup v2 freezer。
檢查凍結狀態變更通知支援 (適用於任何裝置):
在搭載 Android 14 以上版本且具備相容通用核心映像檔 (GKI) 繫結器驅動程式的裝置上,
IBinder.addFrozenStateChangeCallback成功註冊回呼。如果基礎核心繫結器驅動程式不支援凍結通知,這個方法會擲回UnsupportedOperationException。
處理自訂功能
應用程式程序在快取時不應執行任何工作,但部分應用程式可能具備自訂功能,這些功能由快取時應執行的程序提供支援。如果裝置上啟用應用程式凍結功能,且正在執行這類應用程式,系統會凍結快取程序,導致自訂功能無法運作。
如要解決這個問題,請在程序需要執行任何工作前,將程序狀態變更為非快取。這項變更可讓應用程式保持運作。舉例來說,繫結的前景服務或前景狀態都屬於活動狀態。
常見故障模式
應用程式程序凍結時,不當的程序間通訊 (IPC) 或工作排程可能會導致應用程式終止或出現非預期行為。
同步繫結機制資料傳輸至凍結程序
如果用戶端應用程式程序將同步繫結器交易傳送至凍結的伺服器應用程式程序,系統會立即終止伺服器應用程式程序。這樣可避免用戶端執行緒在等待凍結伺服器的回應時無限期封鎖。用戶端執行緒隨後會收到 RemoteException,並觸發所有已註冊的事件監聽器。詳情請參閱 IBinder.linkToDeath。
根本原因:這類失敗通常是由用戶端應用程式中的錯誤所導致。當用戶端繫結至服務時,伺服器程序會繫結至用戶端,且在用戶端繫結前無法進入快取狀態。詳情請參閱 Context.bindService。不過,一旦用戶端呼叫 Context.unbindService,伺服器程序就會遭到快取並凍結。如果用戶端在取消繫結後繼續使用快取 IBinder 參照,可能會與凍結的程序通訊。
如要避免這個問題,請確保用戶端應用程式在呼叫 Context.unbindService 後,立即捨棄 IBinder 參照。
管理用戶端程序的遠端回呼
如果服務和系統元件會維護用戶端程序長期執行的繫結回呼,可以追蹤用戶端凍結狀態,防止同步失敗和非同步緩衝區溢位:
- 註冊狀態變更監聽器:在傳入的用戶端繫結器權杖上使用
IBinder.addFrozenStateChangeCallback,在用戶端程序進入或退出凍結狀態時接收通知。 - 凍結時暫停調度:當用戶端進入
STATE_FROZEN時,暫停將回呼或狀態更新調度至該用戶端。 - 解除凍結後繼續傳送並交付:當用戶端轉換至
STATE_UNFROZEN時,繼續調度回呼,並交付任何必要的批次或合併更新。 - 使用 RemoteCallbackList:使用
RemoteCallbackList的系統服務可以設定凍結的受呼叫者政策,自動暫停及繼續分派回呼,不必維護手動追蹤邏輯。詳情請參閱「系統服務的繫結器凍結建議」。
非同步繫結器交易緩衝區溢位
伺服器應用程式程序在凍結期間收到非同步 (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 表格中。