背景
Android 一直面臨一個核心難題:如何優化進程對有限系統資源(如 CPU、電量)的使用,同時保證用戶體驗。
當進程進入后臺后,它們雖不再貢獻用戶體驗,卻仍可能消耗資源。傳統的殺后臺方案雖然節省資源,但會導致用戶抱怨(如影響多任務和啟動速度)。如今,隨著內存容量提升,進程凍結方案(蘋果的墓碑機制)成為更優解——它既能保留后臺進程(保障快速啟動),又能嚴格限制其 CPU 和電量消耗。
原生方案開關
Google 在android 11 原生代碼開始引入進程凍結方案,可能基于代碼不穩定考慮,整個功能默認是關閉的,然后在android 12 重新做了一些修改后,默認是打開狀態。
可以通過如下方式打開或者關閉原生進程凍結功能:
方式一 開發者選項 “Suspend execution for cached apps”。 可選項一共有三個, Device default, Enabled 和Disabled,android 11 default 是關閉,android 12 default 默認是打開
方式二 adb 命令: adb shell settings put global cached_apps_freezer
以上兩種方式都需要重啟手機生效
原生進程凍結方案框架圖
Framework 上層主要由兩個類控制, OomAdjuster 負責計算APP的oom_score_adj,一旦某個APP的adj大于等于CACHED_APP_MIN_ADJ,就會將凍結該進程的工作委托給CachedAppOptimizer去處理,后者跑在獨立的線程,然后通過jni層接口調用到cgroup的native層。
cgroup抽象層編譯成庫libprocessgroup。抽象層通過往cgroup的文件節點寫入相應的值,來觸發kernel的回調,代碼路徑system\core\libprocessgroup,具體是往/sys/fs/cgroup///cgroup.freeze 節點寫入1凍結,寫入0解凍。
最終kernel cgroup機制的freezer控制子系統真正實現了凍結進程的功能,如下所示:
被凍結的進程,會在freeze_schdule中主動放棄CPU使用權限,將CPU讓給當運行隊列rq中的下一個任務執行,實現的用戶進程的無感凍結。
原生凍結例子
應用A打開到前臺,使用完后,退到后臺,啟動應用B,然后A變成前APP級別,adj變成700,然后再打開應用C,這時如果A沒有啟動服務或者接受廣播之類的,就會變成cached級別應用,那么系統就會設置一個10分鐘的超時,如果10分鐘的時間內,adj的級別沒有變化或者沒有小于cached級別,系統就會觸發凍結A,如果adj變化為小于cached級別,那么就會取消凍結A
原生框架流程
相關代碼路徑
frameworks/base/services/core/java/com/android/server/am/OomAdjuster.java frameworks/base/services/core/java/com/android/server/am/CachedAppOptimizer.java frameworks/base/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp frameworks/base/core/java/android/os/Process.java frameworks/base/core/jni/android_util_Process.cpp
原生凍結細節
第一步,檢查文件鎖"/proc/locks"的狀態。這是為了防止凍結進程持有文件鎖引起死鎖。考慮到一些特殊場景下,進程在被凍結的過程中拿住了文件鎖,凍結成功后還會再檢查一次,發現持有鎖就立刻解凍。
第二步,freeze binder,先是處理當前待處理的binder通信請求,如果進程需要處理的binder請求過多導致在100ms內無法完成,則重新在10分鐘后再次進行凍結流程。如果該過程在100ms內成功,后面禁掉該進程對同步binder請求的接收和處理,以及對異步binder請求的處理。
第三步,setProcessFrozen,調用抽象層提供的API凍結進程。
第四步,再次檢查該進程有沒有需要處理的binder請求,有則解凍進程,然后在10分鐘后再次觸發凍結。這個檢查是為一些特殊場景下,進程在被凍結的過程中產生了binder請求。