Android 应用冻结机制与防冻结方案分析目录
1. 背景介绍
2. 应用冻结机制详解
2.1 什么是应用冻结
2.2 冻结触发条件
2.3 OOM Adjustment 机制
2.4 冻结流程源码分析
3. 防冻结方案详解
3.1 ContentProvider 方案
3.2 前台服务方案
3.3 省电白名单方案
3.4 Persistent 应用方案
3.5 Service 绑定方案
4. 方案对比与评估
5. 实践建议
6. 参考文献
1. 背景介绍从 Android 11 (API 30) 开始,Android 系统引入了应用冻结 (App Freezing) 机制,作为 Cached App Optimizer 的一部分。该机制旨在通过冻结后台缓存应用来减少系统资源消耗,延长电池续航时间,同时保持应用在内存中以便快速恢复。
问题场景在实际应用中,某些后台服务(如计时器、倒计时小工具)在运行一段时间后会突然停止工作,查看日志发现应用被系统主动冻结:
1ActivityManager: freezing 29669 com.example.stopwatch
这种冻结会导致:
后台计时停止
定时任务无法执行
网络连接被挂起
用户体验受影响
2. 应用冻结机制详解2.1 什么是应用冻结应用冻结是一种进程状态管理技术,通过 Linux cgroup freezer 机制暂停进程的执行:
冻结 (Frozen): 进程被暂停执行,不消耗 CPU 资源,但保留在内存中
解冻 (Unfrozen): 进程恢复执行,从暂停点继续运行
与杀死的区别: 冻结的进程仍在内存中,解冻后可立即恢复;被杀死的进程需要重新启动
2.2 冻结触发条件系统在以下条件下会冻结应用(源码位置:CachedAppOptimizer.java):
1234567891011121314151617181920212223242526272829private void freezeProcess(final ProcessRecord proc) { synchronized (mProcLock) { pid = proc.getPid(); // 条件1: ADJ 必须 >= CACHED_APP_MIN_ADJ (900) if (proc.mState.getCurAdj() < ProcessList.CACHED_APP_MIN_ADJ || opt.shouldNotFreeze()) { if (DEBUG_FREEZER) { Slog.d(TAG_AM, "Skipping freeze for process " + pid + " " + name + " curAdj = " + proc.mState.getCurAdj() + ", shouldNotFreeze = " + opt.shouldNotFreeze()); } return; } // 条件2: 不能处于冻结覆盖状态 if (mFreezerOverride) { opt.setFreezerOverride(true); return; } // 条件3: 进程未被冻结 if (pid == 0 || opt.isFrozen()) { return; } Slog.d(TAG_AM, "freezing " + pid + " " + name); }}
核心条件总结:
ADJ >= 900 (CACHED_APP_MIN_ADJ)
shouldNotFreeze() == false
默认延迟时间: 600 秒(10 分钟)
12// 默认冻结延迟时间配置static final long DEFAULT_FREEZER_DEBOUNCE_TIMEOUT = 600_000L; // 10 minutes
2.3 OOM Adjustment 机制OOM Adjustment (简称 ADJ) 是 Android 进程优先级管理的核心机制,值越小优先级越高:
ADJ级别
名称
取值
说明
是否会被冻结
NATIVE_ADJ
native
-1000
Native 进程
❌
SYSTEM_ADJ
sys
-900
System Server
❌
PERSISTENT_PROC_ADJ
pers
-800
Persistent 进程
❌
PERSISTENT_SERVICE_ADJ
psvc
-700
Persistent 服务
❌
FOREGROUND_APP_ADJ
fg
0
前台应用
❌
PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ
50
最近在前台的应用
❌
VISIBLE_APP_ADJ
vis
100
可见应用
❌
PERCEPTIBLE_APP_ADJ
prcp
200
可感知应用(如音乐播放)
❌
PERCEPTIBLE_LOW_APP_ADJ
prcl
250
低优先级可感知应用
❌
BACKUP_APP_ADJ
bkup
300
备份进程
❌
HEAVY_WEIGHT_APP_ADJ
hvy
400
重量级后台进程
❌
SERVICE_ADJ
svc
500
服务进程
❌
HOME_APP_ADJ
home
600
桌面进程
❌
PREVIOUS_APP_ADJ
prev
700
上一个应用
❌
SERVICE_B_ADJ
svcb
800
B List 服务
❌
CACHED_APP_MIN_ADJ
cch
900
缓存应用最小值
✅ 冻结阈值
CACHED_APP_LMK_FIRST_ADJ
950
LMK 首选杀死
✅
CACHED_APP_MAX_ADJ
999
缓存应用最大值
✅
UNKNOWN_ADJ
1001
未知状态
✅
关键点:
ADJ < 900: 不会被冻结
ADJ >= 900: 会在一定时间后被冻结
ADJ 值由 OomAdjuster 动态计算
2.4 冻结流程源码分析步骤1: OomAdjuster 计算 ADJ123456789101112131415161718192021222324252627282930313233343536// OomAdjuster.java@GuardedBy({"mService", "mProcLock"})private void maybeUpdateIsolatedFreeze(ProcessRecord app, @OomAdjReason int oomAdjReason, boolean immediate) { if (!mCachedAppOptimizer.useFreezer()) { return; } if (app.mOptRecord.isFreezeExempt()) { return; } final ProcessCachedOptimizerRecord opt = app.mOptRecord; // 如果已冻结且 shouldNotFreeze 变为 true,立即解冻 if (opt.isFrozen() && opt.shouldNotFreeze()) { mCachedAppOptimizer.unfreezeAppLSP(app, CachedAppOptimizer.getUnfreezeReasonCodeFromOomAdjReason(oomAdjReason)); return; } final ProcessStateRecord state = app.mState; // 核心逻辑:ADJ >= 900 且 shouldNotFreeze = false 时冻结 if (state.getCurAdj() >= FREEZER_CUTOFF_ADJ && !opt.isFrozen() && !opt.shouldNotFreeze()) { if (!immediate) { mCachedAppOptimizer.freezeAppAsyncLSP(app); } else { mCachedAppOptimizer.freezeAppAsyncAtEarliestLSP(app); } } else if (state.getSetAdj() < FREEZER_CUTOFF_ADJ) { mCachedAppOptimizer.unfreezeAppLSP(app, CachedAppOptimizer.getUnfreezeReasonCodeFromOomAdjReason(oomAdjReason)); }}
步骤2: CachedAppOptimizer 执行冻结12345678910111213141516171819// CachedAppOptimizer.javaprivate void freezeProcess(final ProcessRecord proc) { // ... 前置检查 ... try { // 冻结 Binder 接口 freezeBinder(pid, true, FREEZE_BINDER_TIMEOUT_MS); // 冻结进程 Process.setProcessFrozen(pid, proc.uid, true); opt.setFrozen(true); opt.setFreezeUnfreezeTime(SystemClock.uptimeMillis()); mFrozenProcesses.put(pid, proc); } catch (Exception e) { Slog.e(TAG_AM, "Unable to freeze " + pid + " " + proc.processName); }}
3. 防冻结方案详解3.1 ContentProvider 方案原理通过声明 ContentProvider 并调用系统 API getContentProviderExternal(),使系统认为该 Provider 具有外部进程依赖,从而将 ADJ 提升到 0。
关键机制步骤1: 外部进程句柄机制
123456789101112131415161718192021222324252627// ContentProviderRecord.javapublic class ContentProviderRecord { // 记录外部进程对 Provider 的持有 ArrayMap
步骤2: getContentProviderExternal 添加句柄
1234567891011121314// ContentProviderHelper.javaprivate ContentProviderConnection incProviderCountLocked(ProcessRecord r, final ContentProviderRecord cpr, IBinder externalProcessToken, int callingUid, String callingPackage, String callingTag, boolean stable, boolean updateLru, long startTime, ProcessList processList, @UserIdInt int expectedUserId) { // 关键:如果 r == null(通过 getContentProviderExternal 调用),添加外部句柄 if (r == null) { cpr.addExternalProcessHandleLocked(externalProcessToken, callingUid, callingTag); return null; } // ... 正常的 Provider 连接逻辑 ...}
步骤3: OomAdjuster 提升 ADJ
12345678910111213141516171819202122232425// OomAdjuster.javaprivate int computeOomAdjLSP(ProcessRecord app, int cachedAdj, ProcessRecord topApp, boolean doingAll, long now, boolean cycleReEval, boolean computeClients, @OomAdjReason int oomAdjReason, boolean couldRecurse) { // 检查 Provider 是否有外部进程依赖 for (int iproc = ppr.numberOfPublishedProviders() - 1; iproc >= 0; iproc--) { ContentProviderRecord cpr = ppr.getPublishedProviderAt(iproc); // 如果有外部进程句柄,提升到前台应用级别 if (cpr.hasExternalProcessHandles()) { if (adj > FOREGROUND_APP_ADJ) { adj = FOREGROUND_APP_ADJ; // ADJ = 0 state.setCurRawAdj(adj); schedGroup = SCHED_GROUP_DEFAULT; state.setAdjType("ext-provider"); state.setAdjTarget(cpr.name); } if (procState > PROCESS_STATE_IMPORTANT_FOREGROUND) { procState = PROCESS_STATE_IMPORTANT_FOREGROUND; state.setCurRawProcState(procState); } } }}
完整实现代码123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101/** * ADJ 调整器 - 通过 ContentProvider 防止应用被冻结 * 原理:通过 getContentProviderExternal() 使 Provider 具有外部进程依赖, * 从而将应用 ADJ 提升到 FOREGROUND_APP_ADJ (0) */public class AdjAdjuster extends ContentProvider { private static final String TAG = "AdjAdjuster"; private static final String AUTHORITY = "com.example.countdown.provider.AdjAdjuster"; // 引用计数管理 private static final Set
AndroidManifest.xml 配置:
123456789
使用示例:
123456789101112131415161718// 在需要防冻结的场景启动时调用public class CountdownService extends Service { private static final int KEY_COUNTDOWN_SERVICE = 1; @Override public void onCreate() { super.onCreate(); // 启动防冻结保护 AdjAdjuster.onStart(KEY_COUNTDOWN_SERVICE); } @Override public void onDestroy() { // 停止防冻结保护 AdjAdjuster.onStop(KEY_COUNTDOWN_SERVICE); super.onDestroy(); }}
优势
改动极小: 只需添加一个 Provider 类和 AndroidManifest 声明
完全自控: 应用内部可随时启停,无需外部进程配合
ADJ 最低: ADJ = 0,等同前台应用,不会被冻结或杀死
零业务侵入: 不影响原有业务逻辑
引用计数: 支持多场景同时使用,安全可靠
劣势
仅限系统应用: 需要 ACCESS_CONTENT_PROVIDERS_EXTERNALLY 权限(signature|privileged)
需要主动管理: 必须手动调用 onStart() 和 onStop(),忘记调用会导致应用长期占用高优先级
3.2 前台服务方案原理通过启动前台服务(Foreground Service)提升应用优先级,前台服务的 ADJ 通常为 PERCEPTIBLE_APP_ADJ (200)。
实现代码123456789101112131415161718192021222324252627282930313233343536public class AntiFreezeService extends Service { private static final int NOTIFICATION_ID = 1001; private static final String CHANNEL_ID = "anti_freeze_channel"; @Override public void onCreate() { super.onCreate(); createNotificationChannel(); startForeground(NOTIFICATION_ID, createNotification()); } private void createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel( CHANNEL_ID, "防冻结服务", NotificationManager.IMPORTANCE_LOW ); NotificationManager manager = getSystemService(NotificationManager.class); manager.createNotificationChannel(channel); } } private Notification createNotification() { return new NotificationCompat.Builder(this, CHANNEL_ID) .setContentTitle("服务运行中") .setContentText("计时器正在运行") .setSmallIcon(R.drawable.ic_timer) .build(); } @Override public IBinder onBind(Intent intent) { return null; }}
优势
标准方案: Android 原生支持,API 稳定
适用所有应用: 不需要特殊权限
用户感知: 通知栏显示,用户知道服务在运行
劣势
必须显示通知: 无法隐藏,占用通知栏空间
ADJ 较高: ADJ = 200,虽不会被冻结但优先级低于 ContentProvider 方案
用户可关闭: 用户可通过通知栏停止服务
需要修改业务逻辑: 需要将功能迁移到服务中
3.3 省电白名单方案原理将应用加入省电白名单(Power Save Whitelist),系统会设置 shouldNotFreeze = true。
源码机制123456789// OomAdjuster.javaprivate int computeOomAdjLSP(ProcessRecord app, ...) { if (!couldRecurse || !cycleReEval) { // 如果 UID 在白名单中,设置 shouldNotFreeze final UidRecord uidRec = app.getUidRecord(); app.mOptRecord.setShouldNotFreeze(uidRec != null && uidRec.isCurAllowListed()); } // ...}
实现方式方式1: 用户手动添加
1设置 -> 电池 -> 电池优化 -> 选择应用 -> 不优化
方式2: 代码请求(需要用户授权)
123456789PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);String packageName = getPackageName();if (!pm.isIgnoringBatteryOptimizations(packageName)) { Intent intent = new Intent(); intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); intent.setData(Uri.parse("package:" + packageName)); startActivity(intent);}
方式3: 系统预配置(厂商)
1234
优势
无需改代码: 通过配置实现
厂商可控: OEM 可预置白名单
适用所有应用: 不限系统应用
劣势
需要系统配置或用户授权: 普通应用难以自动实现
不同 ROM 差异大: 各厂商实现可能不同
用户可撤销: 用户可随时移出白名单
3.4 Persistent 应用方案原理在 AndroidManifest.xml 中声明 android:persistent="true",使应用成为系统常驻进程,ADJ = -800。
实现方式123456
源码机制1234567891011// OomAdjuster.javaprivate int computeOomAdjLSP(ProcessRecord app, ...) { if (state.getMaxAdj() <= FOREGROUND_APP_ADJ) { // Persistent 应用的 maxAdj 通常设置为 PERSISTENT_PROC_ADJ (-800) state.setAdjType("fixed"); state.setCurRawAdj(state.getMaxAdj()); state.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT); // ... return app.mState.getCurRawAdj(); }}
优势
最高优先级: ADJ = -800,系统级保护
改动极小: 只需添加一个属性
自动重启: Crash 后系统会立即重启
劣势
仅限系统签名应用: 需要 android:sharedUserId="android.uid.system" 或平台签名
资源占用高: 常驻内存,占用系统资源
Crash 频繁重启: 可能导致系统不稳定
3.5 Service 绑定方案原理通过绑定到其他不会被冻结的进程的服务,利用 shouldNotFreeze 的传播机制。
源码机制1234567891011121314// OomAdjuster.javaprivate boolean computeServiceHostOomAdjLSP(ServiceRecord s, ProcessRecord app, ...) { // ... // shouldNotFreeze 会通过绑定关系传播 if (client.mOptRecord.shouldNotFreeze()) { // Propagate the shouldNotFreeze flag down the bindings. if (app.mOptRecord.setShouldNotFreeze(true, dryRun)) { return true; } } // ...}
实现示例123456789101112131415161718192021222324252627282930313233343536// 服务端(不会被冻结的进程)public class KeeperService extends Service { private final IBinder binder = new Binder(); @Override public IBinder onBind(Intent intent) { return binder; }}// 客户端(需要防冻结的应用)public class MyActivity extends Activity { private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { // 绑定成功,shouldNotFreeze 会传播过来 } @Override public void onServiceDisconnected(ComponentName name) { } }; @Override protected void onStart() { super.onStart(); Intent intent = new Intent(this, KeeperService.class); bindService(intent, connection, Context.BIND_AUTO_CREATE); } @Override protected void onStop() { unbindService(connection); super.onStop(); }}
优势
灵活性高: 可以根据需要动态绑定/解绑
适用所有应用: 不需要特殊权限
劣势
需要另一个进程配合: 必须有一个不被冻结的进程提供服务
依赖其他进程状态: 如果服务进程被杀死,保护失效
管理复杂: 需要处理绑定生命周期
4. 方案对比与评估综合对比表
维度
ContentProvider
前台服务
省电白名单
Persistent
Service 绑定
ADJ 值
0
200
不改变(设置 shouldNotFreeze)
-800
继承客户端
防冻结效果
⭐⭐⭐⭐⭐
⭐⭐⭐⭐⭐
⭐⭐⭐⭐⭐
⭐⭐⭐⭐⭐
⭐⭐⭐⭐
防 LMK 效果
⭐⭐⭐⭐⭐
⭐⭐⭐⭐
⭐⭐⭐
⭐⭐⭐⭐⭐
⭐⭐⭐
权限要求
系统权限
无
用户授权/系统配置
系统签名
无
适用范围
系统应用
所有应用
所有应用
系统签名应用
所有应用
改动成本
⭐
⭐⭐⭐
⭐
⭐
⭐⭐⭐⭐
维护成本
⭐
⭐⭐
⭐
⭐⭐⭐⭐⭐
⭐⭐⭐⭐
用户感知
无
有(通知)
无
无
无
可控性
⭐⭐⭐⭐⭐
⭐⭐⭐⭐
⭐⭐
⭐⭐⭐⭐⭐
⭐⭐⭐
资源占用
低
低
无额外占用
高(常驻)
低
ROM 兼容性
⭐⭐⭐⭐⭐
⭐⭐⭐⭐⭐
⭐⭐⭐
⭐⭐⭐⭐⭐
⭐⭐⭐⭐
详细评估4.1 ContentProvider 方案适用场景:
✅ 系统应用(具备 ACCESS_CONTENT_PROVIDERS_EXTERNALLY 权限)
✅ 需要最高优先级(ADJ = 0)
✅ 希望完全自主控制启停
✅ 无需用户感知
不适用场景:
❌ 第三方应用(无权限)
❌ 需要跨进程共享状态
技术特点:
优先级最高,等同前台应用
完全应用内部控制,无需外部配合
代码简单,易于维护
支持引用计数,可多场景使用
推荐指数: ⭐⭐⭐⭐⭐(系统应用首选)
4.2 前台服务方案适用场景:
✅ 所有类型应用
✅ 需要用户知晓服务状态
✅ 可以接受通知栏显示
不适用场景:
❌ 需要隐藏运行
❌ 对通知栏占用敏感
技术特点:
Android 标准方案,API 稳定
用户可见,体验较好
ADJ = 200,优先级较高但不如 ContentProvider
推荐指数: ⭐⭐⭐⭐(第三方应用首选)
4.3 省电白名单方案适用场景:
✅ OEM 厂商预置应用
✅ 客供应用(厂商配置白名单)
✅ 需要用户主动授权的场景
不适用场景:
❌ 需要自动实现的场景
❌ 对不同 ROM 兼容性要求高
技术特点:
无需修改应用代码
厂商可控,适合客供场景
依赖系统配置或用户授权
推荐指数: ⭐⭐⭐(辅助方案)
4.4 Persistent 方案适用场景:
✅ 核心系统服务
✅ 需要系统级保护
✅ Crash 后必须立即重启
不适用场景:
❌ 非系统签名应用
❌ 对资源占用敏感
❌ 应用不够稳定(频繁 Crash)
技术特点:
最高优先级(ADJ = -800)
系统级保护,不会被杀死
资源占用高,影响系统稳定性
推荐指数: ⭐⭐(仅限核心系统服务)
4.5 Service 绑定方案适用场景:
✅ 多进程应用
✅ 已有不被冻结的进程可提供服务
✅ 需要灵活控制
不适用场景:
❌ 单进程应用
❌ 无法保证服务进程不被杀死
❌ 希望简单实现
技术特点:
利用 shouldNotFreeze 传播机制
依赖其他进程状态
管理复杂,需要处理生命周期
推荐指数: ⭐⭐(特殊场景)
5. 实践建议5.1 方案选择决策树1234567┌─ 是系统应用?│ ├─ 是 ──→ 使用 ContentProvider 方案(最优)│ └─ 否 ──┬─ 可以显示通知?│ ├─ 是 ──→ 使用前台服务方案│ └─ 否 ──┬─ 是客供应用?│ ├─ 是 ──→ 联系厂商加入省电白名单│ └─ 否 ──→ 考虑业务重构或使用前台服务
5.2 系统应用推荐实践步骤1: 添加 ContentProvider
1234// 1. 创建 AdjAdjuster.javapublic class AdjAdjuster extends ContentProvider { // ... (完整代码见 3.1 节)}
步骤2: 配置 AndroidManifest.xml
1234
步骤3: 在合适的时机调用
123456789101112131415public class CountdownService extends Service { private static final int KEY_SERVICE = 1; @Override public void onCreate() { super.onCreate(); AdjAdjuster.onStart(KEY_SERVICE); } @Override public void onDestroy() { AdjAdjuster.onStop(KEY_SERVICE); super.onDestroy(); }}
5.3 第三方应用推荐实践步骤1: 使用前台服务
1234567public class TimerService extends Service { @Override public void onCreate() { super.onCreate(); startForeground(NOTIFICATION_ID, createNotification()); }}
步骤2: 提供用户手动加入白名单的入口
12345678910111213141516private void requestBatteryOptimizationExemption() { PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); if (!pm.isIgnoringBatteryOptimizations(getPackageName())) { new AlertDialog.Builder(this) .setTitle("电池优化") .setMessage("为保证计时器正常运行,建议关闭电池优化") .setPositiveButton("去设置", (dialog, which) -> { Intent intent = new Intent(); intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); intent.setData(Uri.parse("package:" + getPackageName())); startActivity(intent); }) .setNegativeButton("取消", null) .show(); }}
5.4 调试方法查看进程 ADJ:
12345678# 查看所有进程的 ADJadb shell dumpsys activity processes | grep -A 5 "com.your.package"# 实时监控 ADJ 变化(方式1:循环查询)while true; do adb shell dumpsys activity processes | grep -A 5 com.your.package; sleep 1; done# 或者使用 watch 命令(如果系统支持)watch -n 1 "adb shell dumpsys activity processes | grep -A 5 com.your.package"
查看冻结状态:
12345678910111213141516# 查看所有冻结的进程(在 CachedAppOptimizer 部分)adb shell dumpsys activity | grep -A 20 "Apps frozen"# 输出示例:# Apps frozen: 3# 1234567890: 12345 com.example.app1# 1234567891: 12346 com.example.app2 (sticky)# 1234567892: 12347 com.example.app3# 格式:<冻结时间戳>:
查看 OOM Adj 日志:
1adb logcat -s ActivityManager:D | grep -E "freezing|unfreezing|ext-provider"
5.5 常见问题Q1: ContentProvider 方案会增加内存占用吗?
A: 几乎不会。Provider 本身是空实现,只是通过外部句柄机制改变了 ADJ 值,不会额外占用内存。
Q2: 忘记调用 onStop() 会有什么影响?
A: 应用会一直保持 ADJ = 0 的高优先级,占用系统资源。建议使用 try-finally 或在组件生命周期方法中确保调用。
Q3: ContentProvider 方案在 Android 12+ 上是否仍然有效?
A: 是的。该机制是 AOSP 的基础设计,在 Android 12、13、14 上均有效。
Q4: 可以同时使用多个防冻结方案吗?
A: 可以,但没有必要。系统会取最低的 ADJ 值,使用 ContentProvider 方案已经是最高优先级。
6. 参考文献源码文件
frameworks/base/services/core/java/com/android/server/am/CachedAppOptimizer.java
frameworks/base/services/core/java/com/android/server/am/OomAdjuster.java
frameworks/base/services/core/java/com/android/server/am/ContentProviderHelper.java
frameworks/base/services/core/java/com/android/server/am/ContentProviderRecord.java
frameworks/base/services/core/java/com/android/server/am/ProcessList.java
相关文档
Android Process and Application Lifecycle
App Standby Buckets
Foreground Services
OOM Adjuster 详解
相关配置12345678// 关键常量ProcessList.CACHED_APP_MIN_ADJ = 900 // 冻结阈值ProcessList.FOREGROUND_APP_ADJ = 0 // 前台应用 ADJProcessList.PERCEPTIBLE_APP_ADJ = 200 // 可感知应用 ADJProcessList.PERSISTENT_PROC_ADJ = -800 // Persistent 应用 ADJ// 默认冻结延迟CachedAppOptimizer.DEFAULT_FREEZER_DEBOUNCE_TIMEOUT = 600_000L // 10 分钟
总结应用冻结机制是 Android 系统为优化资源使用而引入的重要特性。对于需要后台长期运行的应用,选择合适的防冻结方案至关重要:
系统应用: 优先使用 ContentProvider 方案,改动小、效果好、完全可控
第三方应用: 使用 前台服务方案,符合 Android 规范,用户体验好
客供应用: 可联系厂商加入 省电白名单,无需修改代码
核心系统服务: 考虑 Persistent 方案,但需谨慎使用
无论选择哪种方案,都应该充分理解其原理和限制,在保证功能的同时,兼顾系统资源和用户体验。
文档版本: 1.0最后更新: 2025-01-15作者: Android Framework 团队适用版本: Android 11+