Android 15应用冻结与解冻机制:后台进程管理实践
2026/9/17 9:27:35 网站建设 项目流程

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 的 build)

3. 核心机制与实践: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 事件来驱动的。具体流程是:

  1. AlarmManagerService 收到闹钟时间到期的广播。
  2. 系统检查目标应用是否处于冻结状态。
  3. 如果处于冻结状态,系统先把应用进程标记为“临时可运行”。
  4. 将闹钟的 PendingIntent 分发给目标应用。
  5. 应用在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字段。处于冻结状态时,输出通常会包含frozen=true的标记,同时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 的“闲置应用冻结”,不用等系统策略,可以直接用下面的方式加速:

  1. 安装一个侧载 APK(最好是从浏览器下载的、非商店渠道的包)。
  2. 打开一次应用,让它获取到运行权限。
  3. 按 Home 键回到桌面,不要主动划掉后台。
  4. adb shell am set-inactive 包名 true把应用标记为“不活跃”。
  5. 等待系统进入 Doze 状态,或者手动触发一次闲置维护:adb shell dumpsys deviceidle force-idle

这样操作以后,大概率 5 分钟内就能看到系统对目标应用执行冻结。冻结后的表现是:应用在最近任务列表里还在,但点开会重新加载,或者至少会看到明显的加载阶段;同时 logcat 里能抓到冻结日志。

这个实验我强烈建议做系统开发的同学自己跑一遍,因为只有亲手复现过冻结,你在分析用户反馈“后台被杀”时才有直觉判断。我印象很深的一次,是有个用户一直反馈应用收不到推送,抓了日志发现应用在凌晨被冻结了,而推送服务依赖的是 FCM 的High Priority消息,但设备进入 Doze 后高优先级推送的通道也有限制,一层套一层,最后定位到就是 Android 15 把应用归到了“低价值应用”里做深度冻结,而不是推送服务本身的问题。

5. 常见问题与排查技巧实录

这部分是我自己踩过的坑和别人踩了来问我的问题汇总。把它当成速查表用,遇到类似情况能少走弯路。

5.1 应用被冻结后无法收到推送,如何区分是冻结还是网络问题

这是问得最多的一个问题。排查顺序建议是:

  1. 先确认应用有没有在前台服务里做 TCP 长连接。如果是纯 FCM 推送,冻结后会断;如果是自建长连接,冻结后进程线程全部挂起,消息自然收不到。
  2. 检查 logcat 里有没有冻结日志。有冻结日志,看是在什么时候冻结的;没有冻结日志,再看是不是进程被杀了(Process killed日志)。
  3. 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 上比任何版本都来得管用。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询