14、WifiLock 与电源管理:WifiLock 类型、WakeLock 协同、省电策略
说到 Android 的 Wi-Fi 开发,有个东西绕不开——WifiLock。
我刚开始做 Wi-Fi 相关功能时,就踩过它的坑。明明 Wi-Fi 连上了,数据也传得好好的,可手机一锁屏,连接就断了。后来才发现,是系统把 Wi-Fi 模块给休眠了。
嗯,这就要聊到 WifiLock 和电源管理的协同了。
14.1 为什么需要 WifiLock?
Android 系统为了省电,会在屏幕关闭后,逐步让硬件进入低功耗模式。Wi-Fi 芯片也不例外。
但有些场景,比如后台下载大文件、视频通话、OTA 升级,你肯定不希望 Wi-Fi 断掉。这时候,WifiLock 就派上用场了。
说白了,WifiLock 就是一个“请别睡”的令牌。你拿着它,系统就不会把 Wi-Fi 模块关掉。
核心要点:WifiLock 只控制 Wi-Fi 芯片的电源状态,不影响 CPU 休眠。CPU 的电源管理,归 WakeLock 管。
14.2 WifiLock 的三种类型
WifiLock 不是只有一种。Android 提供了三种类型,适用场景各不相同。
| 类型 | 常量 | 说明 | 典型场景 |
|---|---|---|---|
| 满功率锁 | WIFI_MODE_FULL | Wi-Fi 芯片保持全速运行,扫描、连接、数据传输都不受影响 | 大文件下载、视频通话 |
| 扫描锁 | WIFI_MODE_SCAN_ONLY | 只保持 Wi-Fi 扫描功能,不保证连接不断 | 后台位置更新、Wi-Fi 指纹定位 |
| 高频锁 | WIFI_MODE_FULL_HIGH_PERF | 满功率锁的增强版,额外优化延迟和吞吐量 | 实时音视频、在线游戏 |
我个人习惯是:能用扫描锁就别用满功率锁。省电嘛,能省一点是一点。
小技巧:如果你只是需要定时扫描 Wi-Fi 热点,用 WIFI_MODE_SCAN_ONLY 就够了。别一上来就开满功率锁,那玩意儿耗电挺猛的。
14.3 WifiLock 的使用方式
使用 WifiLock 其实很简单,三步走:
- 获取 WifiManager 实例
- 创建 WifiLock 对象
- 加锁 / 解锁
来看代码:
// 获取 WifiManager
WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
// 创建 WifiLock,类型为满功率锁
WifiManager.WifiLock wifiLock = wifiManager.createWifiLock(
WifiManager.WIFI_MODE_FULL,
"my_app:wifi_lock_tag"
);
// 加锁
wifiLock.acquire();
// ... 执行你的 Wi-Fi 相关操作 ...
// 解锁
wifiLock.release();
这里有个坑,我曾经踩过——acquire 和 release 必须成对出现。如果你只 acquire 不 release,Wi-Fi 芯片会一直保持唤醒状态,用户会发现手机耗电异常快。
警告:WifiLock 的 tag 建议用包名加功能名,方便调试时定位问题。别随便写个 "test" 就完事了,线上出问题你根本找不到是谁加的锁。
14.4 WakeLock 协同:CPU 和 Wi-Fi 都要管
WifiLock 只保 Wi-Fi 芯片,不保 CPU。如果你的应用需要同时保持 CPU 和 Wi-Fi 都醒着,那就得配合 WakeLock 一起用。
为什么会这样?你想想看,Wi-Fi 芯片虽然没休眠,但 CPU 如果睡了,你的应用代码也跑不了,数据照样传不出去。
所以,正确的做法是:
- WifiLock:保证 Wi-Fi 连接不断
- WakeLock:保证 CPU 不睡
我建议在后台下载场景中,同时持有这两个锁:
// 获取 PowerManager
PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
// 创建 WakeLock,类型为 PARTIAL_WAKE_LOCK
PowerManager.WakeLock wakeLock = powerManager.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK,
"my_app:wake_lock_tag"
);
// 同时加锁
wifiLock.acquire();
wakeLock.acquire();
// ... 下载任务 ...
// 同时解锁
wakeLock.release();
wifiLock.release();
注意:WakeLock 的类型有很多,PARTIAL_WAKE_LOCK 是最常用的——它只保证 CPU 运行,屏幕可以关。别用 FULL_WAKE_LOCK,那玩意儿会让屏幕一直亮着,太费电了。
14.5 省电策略:能省则省
讲完了怎么用锁,咱们聊聊怎么省电。毕竟,用户最烦的就是 App 偷偷耗电。
我总结了几条实战经验:
- 锁的持有时间越短越好。任务完成了就立刻 release,别拖泥带水。
- 能用扫描锁就别用满功率锁。扫描锁功耗低很多。
- 批量操作合并。比如多个小文件下载,合并成一个任务,减少锁的获取和释放次数。
- 监听屏幕状态。屏幕亮着的时候,Wi-Fi 本来就不会休眠,这时候可以不持锁。
我曾经在做一个 OTA 升级功能时,发现每次升级都持锁 10 分钟以上。后来改成分段下载,每段下载完就释放锁,再配合屏幕状态判断,整体耗电量降了 30%。
避坑指南:不要在主线程中 acquire 锁。WifiLock 的 acquire 是同步操作,如果 Wi-Fi 状态切换慢,可能会卡住 UI。建议在子线程或 Service 中操作。
14.6 知识体系图
下面这张图,帮你理清 WifiLock、WakeLock 和电源管理的关系:
从图中可以看到,应用层通过 WifiLock 和 WakeLock 分别控制 Wi-Fi 芯片和 CPU。两者缺一不可,协同工作才能保证后台任务的稳定运行。
14.7 总结
WifiLock 和 WakeLock 是 Android 电源管理的两个重要工具。用好了,你的 App 既稳定又省电;用不好,轻则功能异常,重则被用户投诉耗电。
我个人建议:每次加锁前,先问问自己——这个锁真的需要吗?能不用就不用,能短用就别长用。这才是真正的省电策略。
最后提醒:Android 9 之后,系统对后台应用持锁的限制越来越严。如果你的 App 目标 API 级别较高,建议使用 WorkManager 或 JobScheduler 来替代手动持锁。这些组件会自动帮你管理电源状态。
公众号:蓝海资料掘金营,微信deep3321