本文介绍 Android 的休眠与唤醒机制,亮点在于不仅覆盖通用移动手机设备,还延伸至当前热门的闺蜜机、智能音箱等 IoT 设备。
一、前言
不知道各位开发朋友是否遇到过下面的场景:
- 设置了 8 点的定时提醒,熄屏后不响了。
- 音乐在后台播放,几分钟后被掐断了。
- 闹钟时间到了,但 APP 并没有被拉起来。
- APP 里点了"上传",然后锁屏把手机放进口袋。过了一段时间后再打开,进度条卡在 30% 不动。
这些现象的背后都是一个原因:休眠和唤醒机制没处理好------Android 在屏幕熄灭后,会逐步收紧对 CPU、网络和后台任务的控制。
本篇会从内核一直讲到 Android 应用层,设备形态除了常见的手机,也会提到投影仪、闺蜜机、TV盒子等 IoT 设备------它们同样运行了 Android 系统,上面的电源管理逻辑和手机既有共性,也有大量差异。
二、熄屏、休眠、睡眠、Doze
先看四个词汇,这是三个不同深度的状态。如果你是 Windows 用户,大概率看到过 Windows 系统设置里的电源设置:关闭屏幕、睡眠和休眠超时。
windows 11 家庭版截图
本篇文章之所以提到它,是因为很多初学者还是会去用 Windows 的电源设置来理解 Android 的,这是不对的:
| Windows术语 | 技术状态 | 耗电/唤醒 | 对应Android |
|---|---|---|---|
| 关闭屏幕 | 只灭显示器 | 系统全醒 | 熄屏(Screen Off) |
| 睡眠 | S3(老机器)/Modern Standby S0ix(新笔记本) | 内存保活,秒醒 | ⭐休眠 / 睡眠(内核 Suspend) |
| 休眠 | S4 | 内存写入硬盘、整机断电 | ⭐Android 手机没有 |
🎓ACPI 把系统电源状态编号 S0~S5,S3 = System/Sleep State 3、S4 = System/Sleep State 4 ...
Android 中文下【休眠/睡眠】经常在混着用,其实都指 Linux Suspend。本文会默认使用【休眠】一词。
Android 自己的三个状态:
- 熄屏(Screen Off):关闭背光,CPU和系统可以全速跑(如音乐播放 APP)。
- 休眠/睡眠(内核 Suspend):Android 里两词同义,都指 Linux 内核 Suspend,CPU 停摆、内存自动刷新保活、外设断电,需要唤醒才能拉醒。
- Doze:Android 6.0 后新增的,是框架层的省电节流,CPU 周期醒、限网、推迟闹钟。
三、Android 休眠机制
搞清楚术语定义,我们来看下面一张 Android 电源分层架构模型:

最底层是 Linux 内核的 suspend。当系统判定"没有人在干活"时,CPU 会进入低功耗状态:屏幕熄灭、CPU 降频,甚至完全停摆------这就是我们说的"休眠"。内核用一个叫 wakeup source(唤醒源) 的机制来记账:只要还有唤醒源被持有,系统就睡不下去;所有唤醒源都释放了,才会真正进入 suspend。
Android 在 kernel 之上封装了一层 WakeLock 。应用通过 PowerManager 申请一把"锁",持有期间系统就不敢彻底休眠,这也是为何熄屏后还可以让 CPU 继续跑的原理。
但 Google 很快发现,一些 APP 滥用 WakeLock,为了保活把手机搞得"永远不睡",这带来了电池续航崩溃、发热严重的后果。于是 Android 6.0(Marshmallow)引入了 Doze 模式 和 App Standby ,在系统层面强制约束后台行为;Android 7 到 12 又一路加码,最终把"精准定时后台任务"这条路几乎堵死,转向引导开发者使用 WorkManager 这类批处理框架。
四、WakeLock 实战
WakeLock 是应用阻止系统休眠最直接的手段。先看它有哪些类型:
| 类型 | 行为 |
|---|---|
| PARTIAL_WAKE_LOCK | 保持 CPU 运行,屏幕和键盘灯可以熄灭。最常用、最安全 |
| FULL_WAKE_LOCK(已废弃) | 保持屏幕和 CPU 都亮着,极费电,已不推荐 |
SCREEN_DIM_WAKE_LOCK/SCREEN_BRIGHT_WAKE_LOCK(已废弃) |
保持屏幕暗/亮,已废弃 |
⚠️
FULL_*、SCREEN_*系列在 API 17 起就被标记废弃。现在唯一建议使用的是PARTIAL_WAKE_LOCK,它适合"屏幕可以灭、但 CPU 不能停"的场景,比如音乐播放、后台录音、长耗时下载。

4.1 安全获取和释放 WakeLock
首先就是在 AndroidManifest.xml 声明权限:
xml
<uses-permission android:name="android.permission.WAKE_LOCK" />
接下来我们展示将 WakeLock 封装到一个工具类:
kotlin
class WakeLockHelper(context: Context) {
private val powerManager =
context.getSystemService(Context.POWER_SERVICE) as PowerManager
private var wakeLock: PowerManager.WakeLock? = null
/**
* 获取 PARTIAL_WAKE_LOCK。
* @param timeoutMs 超时自动释放,防止忘记 release 会导致永远不休眠。
*/
fun acquire(timeoutMs: Long = 10 * 60 * 1000L) {
// 已经持有就不再重复获取,避免引用计数混乱
if (wakeLock?.isHeld == true) return
wakeLock = powerManager.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK,
"MyApp::BackgroundWork" // 包名风格 tag,便于在 bugreport 里定位
).apply {
setReferenceCounted(false) // 非引用计数,release 一次即释放
acquire(timeoutMs)
}
}
fun release() {
wakeLock?.let {
if (it.isHeld) it.release()
}
wakeLock = null
}
}

调用时务必用 try/finally 兜底,确保异常路径也能释放:
kotlin
val helper = WakeLockHelper(context)
helper.acquire()
try {
doCpuIntensiveWork() // 你的后台工作
} finally {
helper.release() // ⭐ 无论成功失败都释放
}
4.2 WakeLock 的常见误用
- 在
Activity里持有 WakeLock 却不释放,用户退出页面后手机一直转; - 用
FULL_WAKE_LOCK来"保持屏幕常亮"------其实用WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON更合适,且不需要任何权限; - 把
WakeLock当"保活"手段长期持有,结果被 Doze 和厂商杀进程策略双重制裁。
五、Doze 模式与 App Standby

Doze 模式 会在设备满足"息屏 + 静止 + 不充电"持续一段时间后触发。一旦进入 Doze,系统会严格限制你的 APP:
- 暂停网络访问;
- 忽略
WakeLock; - 推迟
AlarmManager的闹钟(白名单除外); - 限制 GPS/WiFi 扫描;
- 停止
JobScheduler和SyncAdapter。
但 Doze 并没有"一刀切"地永久冻结后台,系统会周期性地短暂退出Doze,打开一个维护窗口,在这个期间你可以把积压的网络请求、同步任务一次性跑完。
App Standby 则针对"长时间没有被用户主动打开"的APP,它会单独限制其后台网络。
🎓 维护窗口的间隔会随着闲置时间不断拉长,下面图示里的水平绿柱就是不断拉长的间隔。

5.1 Doze白名单
我们可以引导用户把 APP 加入电池优化白名单(官方命名:电池优化豁免列表 ),它可以带来一种有条件的、部分豁免。名单内的 APP 在设备进入 Doze 时,不会被完全冻结网络和唤醒锁,从而可以继续执行一些任务。
但正如我提到的,它的作用有限。如常规的 AlarmManager 闹钟仍然不会触发,JobScheduler 任务也依然会被推迟。而且无论是国内的 ROM 厂商、国外的 Google Play 对这个操作都有各种限制,终端用户也可以轻松关掉它。
kotlin
fun requestIgnoreBatteryOptimization(context: Context) {
val pm = context.getSystemService(Context.POWER_SERVICE) as PowerManager
val packageName = context.packageName
if (pm.isIgnoringBatteryOptimizations(packageName)) return
// 跳转系统设置页,由用户手动授权(无法静默加入)
val intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS).apply {
data = Uri.parse("package:$packageName")
}
context.startActivity(intent)
}

5.2 App Standby
Doze 看的是"设备整体闲不闲",而 App Standby 看的是"这个 APP 你多久没用了"。
一个 APP 什么时候算"被 Standby"?简单说就是"用户很久没主动启动它、它没有前台服务在跑、用户也没和它发的通知交互过、当前不在前台"。满足这些,系统就判定"这 APP 现在不重要",开始延迟它的后台网络、推迟 JobScheduler / SyncAdapter,连普通闹钟(set / setInexact)也会被往后推。
Android 9(Pie, API 28)把"用得越少、限制越狠"做成了分级桶(bucket)------系统按你近期的使用频率,把每个 APP 动态丢进一个桶,桶越低级、限制越重:
| 桶 | 含义 | 限制强度(约值,随版本调整) |
|---|---|---|
| Active(活跃) | 正在用 / 刚用过 | 几乎无限制 |
| Working Set(常用) | 经常用,但此刻不在前台 | 轻限制:作业与网络短暂延迟(分钟级) |
| Frequent(偶尔) | 隔几天用一次 | 中限制:延迟加大(小时级) |
| Rare(很少) | 几乎不打开 | 重限制:后台作业一天最多跑几次,网络几乎掐断 |
| Restricted(受限,Android 12 加) | 用户主动限制 / 长期不互动 | 最狠:连 FCM 高优先级消息都限量 |
六、如何在休眠中"叫醒"系统
了解过休眠机制,下面开始介绍唤醒。
6.1 精确闹钟:AlarmManager
"到点必须叫醒系统干活"的业务十分常见------闹钟、用药提醒、代金券抢购提醒,可以使用 AlarmManager 的 wakeup 类型闹钟。
Android 12(API 31)起:精确闹钟需要声明 SCHEDULE_EXACT_ALARM 权限(闹钟/计时类 APP 可用 USE_EXACT_ALARM),所以先去打开 AndroidManifest.xml 声明权限:
xml
// 大部分"需要精确"的普通应用, Android 12 起
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
// 闹钟/日历/计时器为唯一核心功能的 App,Android 13 起
<uses-permission android:name="android.permission.USE_EXACT_ALARM"/>
kotlin
@SuppressLint("ScheduleExactAlarm")
fun scheduleWakeupAlarm(context: Context) {
val alarmManager =
context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val intent = Intent(context, WakeupReceiver::class.java)
val pendingIntent = PendingIntent.getBroadcast(
context,
0,
intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
// 5 分钟后触发一次,RTC_WAKEUP 会在息屏下唤醒 CPU
val triggerAt = System.currentTimeMillis() + 5 * 60 * 1000L
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// ⭐ setExactAndAllowWhileIdle 能在 Doze 下准时触发
alarmManager.setExactAndAllowWhileIdle(
AlarmManager.RTC_WAKEUP,
triggerAt,
pendingIntent
)
} else {
alarmManager.setExact(AlarmManager.RTC_WAKEUP, triggerAt, pendingIntent)
}
}
接收端(WakeupReceiver.kt)要注意:onReceive 运行在主线程,且 10 秒内必须返回。
记得添加依赖:implementation("androidx.work:work-runtime-ktx:2.9.1")
kotlin
class WakeupReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
// 只做"叫醒 + 派发",重活在 Worker 里跑
val work = OneTimeWorkRequestBuilder<WakeupWorker>().build()
WorkManager.getInstance(context).enqueue(work)
}
}
6.2 非精确、可延迟的批量任务:WorkManger
如果任务不要求太精确,如日志上报、图片压缩、数据同步,则优先使用 WorkManager。它会自动在系统条件允许时批量执行(包括Doze 期间),最省电也最不容易被系统干掉,并且自动兼容 Doze、App Standby 和厂商限制。
kotlin
class WakeupWorker(context: Context, params: WorkerParameters) : Worker(context, params) {
override fun doWork(): Result {
// 在这里做真正的后台工作,运行在后台线程
uploadLogs()
return Result.success()
}
private fun uploadLogs() {
// 你的业务逻辑
}
}
// 调度一个每 15 分钟一次的周期任务(WorkManager 最短间隔就是 15 分钟)
fun schedulePeriodic(context: Context) {
val request = PeriodicWorkRequestBuilder<WakeupWorker>(15, TimeUnit.MINUTES)
.build()
WorkManager.getInstance(context)
.enqueueUniquePeriodicWork(
"daily-sync",
ExistingPeriodicWorkPolicy.UPDATE,
request
)
}

七、嵌入式 IoT 类型设备的休眠和唤醒
这几年 Android 系统被大量用在投影仪、拉杆音箱、视频机、闺蜜机、TV 盒子上。不同于手机这类"省电优先"的设备,闺蜜机、拉杆音箱等有自己的电源管理目标。
7.1 IoT 终端设备和手机不一样
手机是"电池 + 随时随身",电源管理第一目标是省电。但许多消费级 IoT 终端设备不是:
-
投影仪:大多一直插电,但待机时必须把灯泡/光机彻底断电以散热和节电,SoC 进入深度 suspend,靠红外遥控或电源键唤醒。
-
智能拉杆音箱:带大电池,但"关机"状态下必须几乎零耗电,同时又要能被蓝牙连接、AUX 接入、按键"秒唤醒"。
这些设备同样有"休眠-唤醒",但关注点更多从"省电"变成了"待机低功耗 + 可靠唤醒 + 外设协同"。
7.2 全局不休眠
还有些设备就要求:全局不休眠。这类设备通常预装系统级 APP 或拥有系统签名,可以直接改全局设置:
kotlin
// 需要系统权限(WRITE_SETTINGS / 系统签名应用),普通第三方 App 会抛异常
Settings.Global.putInt(
context.contentResolver,
Settings.Global.STAY_ON_WHILE_PLUGGED_IN,
BatteryManager.BATTERY_PLUGGED_AC or BatteryManager.BATTERY_PLUGGED_USB
)
// 或者把休眠超时直接设为最大值
Settings.System.putInt(
context.contentResolver,
Settings.System.SCREEN_OFF_TIMEOUT,
Int.MAX_VALUE
)
7.3 瑞芯微平台的唤醒
我们以瑞芯微平台为例,瑞芯微平台(RK3399 / RK3566 / RK3568 / RK3588 等)的 Android 系统,休眠由 Linux 内核的 suspend 驱动,Android 通过 system/core/libsuspend 写 /sys/power/autosleep 触发。设备能不能被某个外设唤醒,由设备树(Device Tree)里是否把它标记为唤醒源决定------这是 BSP / 固件工程师的地盘,有兴趣的朋友可以继续自行搜索学习,本篇讲解的不多。
典型的可唤醒外设:
-
电源键 / 实体按键 :
gpio-keys里标记wakeup-source; -
红外遥控(IR) :投影仪最常见的唤醒方式,IR 驱动节点加
wakeup-source; -
RTC:PMIC(如 RK817)内置 RTC,可做定时开机/唤醒;
-
网络:以太网 PHY 的 WoL(Wake-on-LAN)、部分 WiFi 唤醒;
-
蓝牙:UART 主机的 wake GPIO,实现"蓝牙连接即唤醒"。
下面是一个示意性的设备树片段(不同板级 SDK 节点名会有差异,但模式一致),展示如何把红外和电源键声明为唤醒源:
text
// 红外遥控:待机时靠它唤醒
&ir_rx {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&ir_rx_gpio>;
wakeup-source; // ⭐ 关键:让 IR 成为唤醒源
};
// 实体电源键
gpio_keys: gpio-keys {
compatible = "gpio-keys";
power-key {
gpios = <&gpio0 RK_PA5 GPIO_ACTIVE_LOW>;
linux,code = <KEY_POWER>;
label = "power";
wakeup-source; // ⭐ 电源键可唤醒
};
};
// PMIC 内置 RTC:可做定时唤醒
&rk817 {
rk817_rtc: rtc {
wakeup-source;
};
};
7.4 待机墙和快速恢复
投影仪、闺蜜机不能真的"黑屏睡着",行业惯例是做低功耗待机页(Standby Screen):显示时钟/海报的低亮度界面,配合 Dream Service(屏保框架)实现,既省电又保留了"一按即亮"的体验,还规避了深睡后被杀进程的风险。
7.5 应用层如何做
在嵌入式设备上做 APP(比如给投影仪写一个 launcher、给音箱写一个播放器),我们选择的"武器"依然是 PowerManager 和 WakeLock,但用法要贴合手中的设备:
-
播放 / 推流时持
PARTIAL_WAKE_LOCK:和手机一样,屏幕可以灭,但 CPU 不能停。投影仪放片、音箱播歌时务必持有并在结束时释放。 -
主动休眠用
PowerManager.goToSleep():注意它和 WakeLock 方向相反------WakeLock 阻止睡,goToSleep()主动让系统睡(常用于"遥控器待机键"逻辑)。 -
远程唤醒 :远程唤醒要靠局域网指令:设备开放一个本地 socket / HTTP 服务,或被同局域网 App 发 UDP 包唤醒+ 内核把对应网口配置为 WoL 唤醒源。
-
和固件团队对齐唤醒源清单 :APP 再怎么写,设备整体能不能被某外设唤醒,最终取决于设备树 里的
wakeup-source。这是上层和 BSP 的接口契约,APP 开发前先要获取这份清单。
八、调试技巧
一些常见的调试命令:
bash
# 查看当前 WakeLock 持有情况(谁在阻止休眠,一目了然)
adb shell dumpsys power | grep -i wake
# 查看 Wakefulness 状态与休眠原因
adb shell dumpsys power | grep -E "mWakefulness|Last go to sleep reason"
# 模拟进入 Doze(测试闹钟/任务是否会被推迟)
adb shell dumpsys deviceidle force-idle deep
# 观察 Doze 状态变化
adb shell dumpsys deviceidle get deep
# 退出模拟,恢复设备
adb shell dumpsys deviceidle unforce
# 查看系统支持的休眠状态(mem / freeze)
cat /sys/power/state
# 手动强制进入 suspend(调试用,生产别乱敲)
echo mem > /sys/power/state
# 看某个输入设备是否允许作为唤醒源(yes/no)
cat /sys/class/input/eventX/device/power/wakeup
# 内核层所有唤醒源的命中统计,定位"是谁一直把系统叫醒"
cat /sys/kernel/debug/wakeup_sources

九、结语
Android 休眠与唤醒的本质,是应用需求与系统省电策略的博弈 。理解熄屏、休眠、Doze 三层模型后,如何设计业务就清晰了:保持屏幕常亮清晰优先考虑 FLAG_KEEP_SCREEN_ON,穿透休眠用精确闹钟 AlarmManager,可延迟任务交给 WorkManager,系统级需求走设备定制。手机场景要"顺从"Doze,而投影仪、TV 盒子、音箱这类常插电设备则要"驯服"休眠,做出符合产品形态的待机体验。把这些机制吃透,我们写出的就不只是能跑的代码,而是让用户设备既可靠又省电的代码。
十、参考资料
- https://developer.android.google.cn/training/monitoring-device-state/doze-standby?hl=zh-cn
- https://source.android.google.cn/docs/core/power/mgmt?hl=zh-cn
- https://developer.android.com/reference/android/os/PowerManager
- https://developer.android.com/reference/android/app/AlarmManager
- https://developer.android.com/topic/libraries/architecture/workmanager
- https://developer.android.google.cn/develop/background-work/background-tasks/awake?hl=zh-cn
- https://android-docs.cn/training/monitoring-device-state/doze-standby
- AOSP 电源管理源码:
system/core/libsuspend、frameworks/base/services/core/java/com/android/server/power/(wakeup source / wakelock 实现) - Linux 内核文档 --- power / wakeup-sources:
https://www.kernel.org/doc/Documentation/power/wakeup-sources.txt - Rockchip 官方 SDK 与内核文档(电源管理、suspend-to-RAM、
wakeup-source设备树属性,随板级 BSP 提供)
