Android 15 应用冻结与解冻机制实际场景下的行为分析与排查心得最近不少做系统开发和定制 ROM 的朋友都在聊 Android 15代号里的 V 版本里应用冻结机制的变化。尤其是一些拿到 Pixel 和首批 Android 15 机型的用户发现一部分侧载应用装上没几天后台就不再运行了点开要重新加载通知也经常延迟。这个现象背后正是 Android 15 对应用冻结策略的一次重要收紧——Google 把原本针对后台缓存进程的冻结机制扩展到了更多“不活跃”的侧载应用上目的倒不是要搞死谁而是为了省电和减少内存占用。这篇内容我不打算讲源码级别的调度器实现那样太枯燥。我想从一个做 ROM 和系统优化的从业者视角把 Android 15 里应用的“冻结”和“解冻”到底是什么、在什么场景下会触发、哪些情况下会被系统重新唤醒以及你在用 ADB 或系统工具手动冻结/解冻时会踩到什么样的坑一次说清楚。不管你是做系统开发、搞定制 ROM还是普通用户想用 ADB 冻结那些启动自启乱跳的应用这篇文章应该都能给到有用的东西。1. 从一个核心问题说起Android 15 的“冻结”到底动了什么先说一个容易被混淆的问题。很多人以为 Android 的“冻结”只是把应用进程暂停其实在 Android 15 里这套机制早就不只是“暂停”这么简单了。1.1 “冻结”和“杀后台”是两码事我们日常说的“杀后台”在 Android 系统里的本质是回调onTrimMemory()或者直接终止进程让进程彻底死掉下次启动就是冷启动一切状态归零。而“冻结”操作内核层面对进程组挂起进程还在内存还在但 CPU 时间片不再分配给它定时器、消息循环、网络回调这些全部暂停执行。思想很简单进程是活的但它的“时间”被停住了。这个机制在 Android 里其实已经存在好几个版本了最早的推进可以追溯到 Android 10 时代引入的“APP_FREEZER”机制。Android 15 的“V 版本”内部代号 Vanilla Ice Cream 的缩写字头在这个基础上做了一件更激进的事以前冻结机制主要盯后台缓存进程Android 15 会把判定范围扩大到“用户长时间未打开”的应用上尤其是侧载sideload来源的 APK。系统通过 Play Protect 的实时扫描和质量反馈给每个应用生成一个“用户信任度”的评估评估不够高的应用只要在后台闲逛超过一段时间就会直接被冻结。1.2 Android V 的冻结策略有什么不同Android 15 里针对应用的冻结策略和之前版本最大的区别在于它引入了更细的“进程状态判断”和“冻结优先级”。传统的 Android ActivityManager 会依据进程对用户的重要性排出 oom_adj 等级等级越低代表越不重要越容易被回收。Android 15 在此基础上叠加了一层“活跃度模型”系统不只关注 app 当前在不在前台还会看它被用户主动使用的频率和最近一次使用时间。换句话说以前一个应用只要回到后台过一段时间进入 cached 状态系统就算冻结它也是“低温冻结”——进程还在但可以随时被杀掉。而 Android 15 里很多应用遇到的是“高温冻结”——系统会强制挂起它的全部线程同时给它的网络访问、唤醒锁、Alarm 闹钟机制等加上限制这个状态比单纯的 cached 要更深你需要主动点开应用才能让它彻底“暖和”过来。那么问题来了什么场景下系统会决定冻结一个应用什么场景下又会解冻这正是我在实际调试中花最多时间整理的。2. 冻结发生的核心场景与实际触发条件为了把这个讲透我直接把 Android 15 内部做冻结判定的几个关键场景列出来这些也是你在复现问题、调试 bug 时最需要关注的地方。2.1 后台缓存进程的“标准冻结”场景这是最经典也最温和的一种。应用进入后台后ActivityManager 会周期性检查进程状态。如果进程已经无法被用户感知也没有前台服务在运行系统就把它的processState标记为PROCESS_STATE_CACHED。在 Android 15 的默认配置里这类进程会进入冻结候选名单系统在内存紧张、电量不足、或者设备闲置的时候会批量冻结。具体到触发条件和这几个参数有关ro.lmk.critical_upgrade是否开启persist.sys.fflag.override.settings_enable_monitor_phantom_procs这类 FF 开关的配置设备当前的内存水位和电池电量屏幕熄灭后的“闲置时间”累计值我自己在调试时发现一个规律屏幕熄灭超过 30 分钟是一个明显的临界点。一旦跨越这个时间系统会启动一轮“闲置维护”对缓存进程做大规模的冻结操作这轮操作在 logcat 里通常能看到Freezing process字样。2.2 “不活跃应用”的深度冻结Android 15 新玩法这是 Android 15 里最值得展开的部分。系统通过 PackageManager 和各应用商店的反馈数据维护一个“应用分类表”把应用分成几档系统应用、官方商店应用、高度信任的侧载应用、一般信任的侧载应用、低信任度的侧载应用。对于后两种Android 15 会启用一个叫“Unused Apps Freezing”的策略。规则大概是这样的应用连续 30 天未被用户主动打开应用没有活跃的通知渠道常驻应用没有正在运行的前台服务或媒体会话应用没有在系统“电池优化白名单”中满足以上条件系统会在某个夜间闲置时段把它冻结。冻结以后这个应用的网络访问会被切断唤醒锁会被释放Alarm 闹钟在待机状态下不会触发通知栏里的常驻通知也会被移除。这个策略对用户最大的感知就是“我明明没有卸载它它怎么就‘死’了”尤其在 Android 15 上这个行为和 Play Protect 深度绑定如果你的应用是从网页直接下载的 APK首次安装时 Play Protect 弹过“未检测到有害信息”但也没有详细介绍开发商那么这种应用的信任度评级往往很低冻结合法性最高。2.3 内存压力和电池低电量下的“主动冻结”前面讲的都是有明确规则的“定时冻结”还有一种情况属于“应急冻结”。当系统物理内存可用量跌破阈值常见的是不足 12%-15%内核的 lmkd 会优先尝试冻结那些已经处于 cached 状态的进程而不是直接杀掉它们。这么做的动机很好理解杀掉进程意味着下次启动要重新创建代价高冻结虽然不能立刻回收内存但它能阻止进程继续分配新的内存页相当于把内存耗散的源头先堵住。电池低电量一般是低于 20%时也会触发类似逻辑。系统会开启省电模式同时那些长期在后台跑的应用会被逐个冻结。如果应用设置了“不受电池优化限制”那么它会逃过这一劫但代价是系统会生成一个高耗电通知告诉用户。2.4 厂商定制场景一加、小米、vivo 的“加强版冻结”说完了原生 Android 15必须提一下国内厂商基于 Android 15 的 ROM 定制。这些厂商普遍会把自己的“智能后台管理引擎”和原生冻结机制叠加在一起导致行为更激进。以小米的 HyperOS澎湃 OS为例它的“省电策略”里有一个“深度睡眠”模式其实就是把原生 Android 的 App Freezer 参数调得更激进同时加上了自己的“应用行为记录”判断。只要是用户一周没打开过的应用系统会在锁屏后的半小时内直接冻结而且不经过原生系统的“闲置时间”累计流程。所以你可能在原生 Android 15 上调得好好的应用兼容性刷到澎湃 OS 上就“后台被杀得干干净净”——这就是厂商叠加策略导致的差异。做兼容性适配的同学一定要把厂商 ROM 的冻结策略当成独立的系统来测不能只以 AOSP 原生的行为为标准。2.5 ADB 手动冻结的场景和注意事项除了系统自动冻结不少用户会自己用 ADB 冻结应用尤其是那些自带全家桶互相唤醒的国内应用。“红米 K70 能用 ADB 冻结吗”这个问题在社区里很常见答案是能但和 Android 15 的系统冻结机制完全是两回事。你用pm disable-user --user 0 包名或者pm suspend 包名做的操作本质上已经是“停用应用”而不是“冻结进程”。停用以后应用的四大组件都不会被系统拉起桌面图标也会从启动器里消失。这种操作的力度比系统冻结大得多系统冻结进程还留着只是不调度停用是直接把应用从系统里“半卸载”了。我在不少机型的测试中发现如果用户先用 ADB 停用了某个应用然后又想用 Android 15 的冻结机制去精准控制它两边会打架。原因是pm suspend会把应用标记为FLAG_SUSPENDED而原生冻结机制碰到这个标志位会直接跳过冻结流程——因为应用已经物理不可用了不需要再冻结。所以如果你要用 ADB 做应用管控建议分清目标只是限制后台耗电、不想让它乱跑用cmd appops set 包名 RUN_IN_BACKGROUND ignore想彻底停用、禁止自启和互相拉起用pm disable-user --user 0 包名想模拟系统冻结测试应用在冻结状态下是否崩溃用am freezer相关的调试命令需要 debuggable 的 build3. 核心机制与实践Angry 的“解冻”路径系统如何唤醒冻结应用刚才讲了半天冻结但这套机制真正难搞的是解冻。很多用户抱怨“冻结以后应用收不到消息点开也没反应”其实就是解冻路径没走通。3.1 用户在桌面点击图标最常规的解冻触发最常见的解冻场景就是用户主动打开应用。当你点击桌面图标Launcher 会调用startActivity()ActivityTaskManager 在解析 Intent 时发现目标进程当前处于 frozen 状态就会先向内核的 freezer 驱动发送解冻信号把进程从 FROZEN 状态恢复到 RUNNING然后才继续走正常的 activity 启动流程。这个过程从用户感知上是“秒开”的因为进程虽然没有在跑但内存数据都还在热启动的速度远快于冷启动。如果系统没有做冻结应用在后台放了一晚上进程被 LMKD 杀掉了那么点开就是冷启动要经历完整的 Application 初始化。所以冻结机制的初衷之一其实是保持热启动的“快感”。3.2 通知点击与 FGS系统不会无条件解冻这里有个大坑。Android 5.0 之后通知系统用PendingIntent拉起应用是常态。但在 Android 15 的冻结机制下面一个处于冻结状态的应用如果发出了通知理论上不会因为冻结时网络和闹钟都被掐了用户点击通知系统会解冻应用但要先做一轮“诊断”。具体来说系统会检查这个通知是不是应用在被冻结前通过notify()方法已经在通知栏里挂着的。如果是点击通知会先解冻进程再回调onNewIntent()或创建新的 Activity。但如果是应用被冻结以后某些系统组件比如媒体会话、电话服务试图拉起它系统会根据拉起者的优先级来做“热解冻”或者“冷解冻”优先级高的系统服务可以立即解冻第三方应用则会被阻塞在等待队列里。还有一种经常被误读的场景是前台服务Foreground Service。很多人以为只要应用启动了 FGS就不会被冻结。错了。Android 15 里FGS 只能保证应用进程的优先级高于 cached但如果应用进入后台后 FGS 类型不对比如用了dataSync而不是mediaPlayback系统依然可以在特定场景下冻结进程只是这种冻结是“冻结线程”而不是“终止服务”。等 FGS 需要执行任务时再通过onTimeout或者Service.onStartCommand回调解冻。3.3 闹钟、消息推送和系统广播如何实现“定向解冻”Android 15 里有一个重要的设计不是所有解冻都需要“完全解冻”。系统把冻结状态做了一个更细的切分——分为“普通冻结”和“深度冻结”。普通冻结状态下系统可以针对某个特定组件做“定向解冻”比如闹钟需要响铃了系统会暂时解冻 AlarmManager 相关的 handler让闹钟触发然后立刻重新冻结。这个机制实际执行时是通过PowerManager下的 wakeup 事件来驱动的。具体流程是AlarmManagerService 收到闹钟时间到期的广播。系统检查目标应用是否处于冻结状态。如果处于冻结状态系统先把应用进程标记为“临时可运行”。将闹钟的 PendingIntent 分发给目标应用。应用在onReceive()或onAlarm()回调执行完毕后系统自动恢复冻结。这套定向解冻的设计避免了“为了响个闹钟就得让整个应用满血复活”的资源浪费。但代价就是应用开发者在后台执行的逻辑如果碰上了“临时可运行”窗口可能刚启动一个异步任务系统就把进程重新冻结了任务半路夭折。这个也是开发者经常抱怨 Android 15 后台执行限制更严格的直接原因。3.4 系统设置里的“解除冻结”入口和厂商适配差异普通用户如果发现某个应用被冻结了想手动解冻在原生 Android 15 里其实找不到一个叫“冻结列表”的设置页。最直接的路径是“设置 → 应用 → 找到对应应用 → 强行停止”这个能解锁被系统冻结的进程。但要注意这个操作只是解除了一次性的冻结状态如果应用的“不活跃”判定没有改变系统后台还是会再次冻结它。国内厂商 ROM 相对好一点一般在“设置 → 应用管理 → 特殊应用权限 → 电池优化”或者“后台管理”里会有一个“允许自启动/允许后台运行”的选项关闭“智能限制”等同于把应用从冻结候选名单里拉出来。我自己在测试 Android 15 的各类国内 ROM 时发现厂商对解冻路径的适配差异非常大。有的把解冻入口藏得极深你在应用详情页里根本找不到有的则会弹一个系统级的“应用被限制运行”的通知点通知里的“允许运行”就能解冻。4. 实操过程里的核心环节与避坑经验讲到实操必然要涉及你到底怎么观察冻结和解冻的过程。我给一套自己经常用的方法不依赖厂商私有接口AOSP 原生的就行。4.1 用 dumpsys 观察进程冻结状态拿到一台 Android 15 的机器开启开发者选项和 USB 调试连接电脑后执行adb shell dumpsys activity processes在输出里搜索你想要观察的包名重点是看它的processState字段。处于冻结状态时输出通常会包含frozentrue的标记同时lastCpuTime会停留在冻结前的时间点附近不再增长。还有一个更直观的命令adb shell dumpsys activity exit-info这个命令会输出系统为什么让一个应用退出或冻结记录了最近被冻结的应用和被冻结时的 reason非常有用。如果你想直接看冻结状态机需要系统里有 root 权限然后执行adb root adb shell cat /sys/fs/cgroup/freezer/tasks直接查看内核层的 freezer 状态。注意不同内核版本的 cgroup 路径可能不同Android 15 上通常是cgroup 的 freezer 子系统adb shell ls /sys/fs/cgroup/如果能看到freezer目录说明该内核已经启用了 freezer cgroup。再去/sys/fs/cgroup/freezer/下翻子目录就能看到系统把哪些进程组放了进去。4.2 用 logcat 抓取冻结/解冻事件在 Android 15 上系统冻结和解冻应用时通常会有系统 log。用下面的过滤方式能快速看到adb logcat -s ActivityManager:I | grep -i freez或者直接抓所有带 freezer 关键字的日志adb logcat | grep -iE freez|unfreez实测时经常能看到类似这样的日志ActivityManager: Freezing process pid 12345: com.example.app ActivityManager: Unfreezing process pid 12345: com.example.app如果系统用的是 kernel 层的 freezer还会在 kernel log 里看到adb shell dmesg | grep -i freezer输出类似Freezing user space processes ... Restarting tasks ... done.这里有个小经验Freezing user space processes这类日志往往是整个系统进入休眠suspend时的全局冻结和应用级冻结不同。前者是所有用户态进程都被冻结后者是只针对某些进程组的定向冻结。区分不开容易把问题查偏。4.3 常用调试命令速查表目标命令说明查看应用进程状态adb shell dumpsys activity processes | grep 包名能看到 processState、frozen 标志查看应用是否被系统判定为“不活跃”adb shell dumpsys usagestats查看应用最近的使用时间记录手动停用应用adb shell pm disable-user --user 0 包名等同“卸载”四大组件不再启动重新启用应用adb shell pm enable 包名恢复应用可用查看最近被冻结的应用adb shell dumpsys activity exit-info可看到冻结原因关闭系统对特定包名的冻结优化adb shell cmd appops set 包名 RUN_ANY_IN_BACKGROUND ignore不完全等于锁后台但能降低冻结概率强制把某个进程拉回前台adb shell am start -n 包名/Activity名测试解冻后的应用行为4.4 实际测试中的一个小实验自己制造冻结场景如果你想复现 Android 15 的“闲置应用冻结”不用等系统策略可以直接用下面的方式加速安装一个侧载 APK最好是从浏览器下载的、非商店渠道的包。打开一次应用让它获取到运行权限。按 Home 键回到桌面不要主动划掉后台。用adb shell am set-inactive 包名 true把应用标记为“不活跃”。等待系统进入 Doze 状态或者手动触发一次闲置维护adb shell dumpsys deviceidle force-idle。这样操作以后大概率 5 分钟内就能看到系统对目标应用执行冻结。冻结后的表现是应用在最近任务列表里还在但点开会重新加载或者至少会看到明显的加载阶段同时 logcat 里能抓到冻结日志。这个实验我强烈建议做系统开发的同学自己跑一遍因为只有亲手复现过冻结你在分析用户反馈“后台被杀”时才有直觉判断。我印象很深的一次是有个用户一直反馈应用收不到推送抓了日志发现应用在凌晨被冻结了而推送服务依赖的是 FCM 的High Priority消息但设备进入 Doze 后高优先级推送的通道也有限制一层套一层最后定位到就是 Android 15 把应用归到了“低价值应用”里做深度冻结而不是推送服务本身的问题。5. 常见问题与排查技巧实录这部分是我自己踩过的坑和别人踩了来问我的问题汇总。把它当成速查表用遇到类似情况能少走弯路。5.1 应用被冻结后无法收到推送如何区分是冻结还是网络问题这是问得最多的一个问题。排查顺序建议是先确认应用有没有在前台服务里做 TCP 长连接。如果是纯 FCM 推送冻结后会断如果是自建长连接冻结后进程线程全部挂起消息自然收不到。检查 logcat 里有没有冻结日志。有冻结日志看是在什么时候冻结的没有冻结日志再看是不是进程被杀了Process killed日志。用adb shell dumpsys deviceidle查看设备当前是否处于 deep idle 状态。设备级休眠和应用级冻结是两套机制经常被混为一谈。我遇到过最典型的案例某个应用在 Android 14 上后台一切正常升级到 Android 15 后推送全丢。排查了半天发现不是应用冻结导致的而是设备进入 Doze 后系统限制了对setExactAndAllowWhileIdle()闹钟的使用频率推送方用的心跳包被系统拦截了。解决办法是把应用的电池优化改成“不受限制”或者引导用户把应用加到厂商 ROM 的“锁定后台”名单里。5.2 解冻后应用出现白屏、闪退、数据丢失这往往不是解冻“解坏”了而是应用自身对生命周期管理不当。应用在冻结状态下时间停滞解冻时所有线程同时恢复运行那些依赖System.currentTimeMillis()做超时判断、或者用Handler.postDelayed()做逻辑漏判的代码会突然发现“时间跳跃”了。轻则界面刷新异常重则直接抛异常闪退。你可以在解冻前先给应用发一个ACTION_CLOSE_SYSTEM_DIALOGS广播试试有些应用在收到这个广播后会重新初始化 UI。但更根治的办法是开发者自己在Application.onTrimMemory()里做状态保存不要依赖系统在冻结前给你足够的时间来做保存操作——系统冻结进程的速度极快根本来不及等你优雅退出。5.3 “我明明没有冻结它它却被冻结了”的系统背锅排查很多用户以为是自己误操作了pm disable但实际是系统自动冻结。这种问题的排查思路是先看dumpsys activity exit-info里的冻结原因再查sessions/usagestats里应用最近一次使用时间最后确认是否满足“30 天未使用”这个硬条件如果确实被系统自动冻结了且应用对用户很重要建议引导用户把应用加到“不受电池优化限制”的白名单里。这个操作在原生 Android 上会弹系统提示说明“允许应用随时保持活动可能会导致耗电”但能有效防止大部分自动冻结场景。需要提醒的是白名单只能阻止“自动闲置冻结”挡不住内存压力下的“应急冻结”。当系统可用内存真的告急时白名单应用的进程同样可能被冻结甚至被杀只是优先级比其他应用高一点而已。5.4 常见问题速查表现象可能原因推荐排查方式应用点开重新加载进程被冻结后解冻或进程已被杀dumpsys activity processes看 frozen 标志推送收不到应用被冻结/设备进入 Doze/网络权限被限制logcat 抓冻结日志dumpsys deviceidle 查设备状态应用自启动失效应用被停用disable不只是冻结pm list packages -d查看已停用应用闹钟不响冻结状态下 Alarm 被延迟或 setExact 被限制dumpsys alarm查看闹钟下次触发时间冻结后无法解冻系统处于深度 Doze解冻操作被挂起先点亮屏幕唤醒设备再等系统退出 idle投屏找不到设备投屏服务应用被冻结mDNS 广播无法发送把投屏应用加入电池优化白名单5.5 我看过的“神操作”用冻结机制来做“平替”最后分享一个有意思的用法。有一些用户拿 Android 15 的冻结机制当“免 root 的绿色守护”用。具体做法是用adb shell cmd appops set 包名 RUN_ANY_IN_BACKGROUND ignore禁止应用在后台运行然后用adb shell am set-inactive 包名 true手动把应用标记为冷门应用再配合系统自带的电池优化把那些启动自启、全家桶互相唤醒的应用“物理消灭”。这个方案比用pm disable温和不会导致应用图标消失也不会影响手动点击打开时的功能完整性而且完全不依赖 root 权限。在红米 K70、小米 14 这类澎湃 OS 机型上实测下来市面上绝大多数“全家桶”应用都能被这套组合拳按得死死的。不过要强调的是am set-inactive这个命令需要应用在系统里已经运行过至少一次而且系统重启后标记可能被重置为 false。如果确实需要长期保持建议写一个开机广播的 Tasker 脚本或者用 Shizuku 在每次开机后自动执行一次。6. 关于冻结机制我自己的一点实际体会Android 的冻结和解冻机制从内核的 freezer 到 ActivityManager 的应用状态管理再到厂商 ROM 的加强策略每一层都有各自的行为边界。很多用户和开发者被“后台被杀”坑到崩溃其实大多数时候都是没分清楚“进程被冻结”和“进程被杀死”之间的区别也没搞清楚系统到底依据什么规则做的决定。做了这么多年系统优化我的体会是不要神话冻结机制的省电效果也不要把它当成洪水猛兽。它本质上是系统在资源有限性、用户可感知流畅度和后台任务可靠性之间做的一个动态权衡——权衡的尺子在 Google 手里也被厂商捏来捏去。你能做的就是搞清楚自己手上的设备里这把尺子是怎么量的然后对症下药。如果你只是想让自己常用的应用不被冻结最省心的策略就是应用商店里下载的、常用的应用直接进电池优化白名单侧载来的、偶尔用的应用别指望它在后台给你推消息。这一条建议在 Android 15 上比任何版本都来得管用。