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 其实很简单,三步走:

  1. 获取 WifiManager 实例
  2. 创建 WifiLock 对象
  3. 加锁 / 解锁

来看代码:

// 获取 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 与电源管理协同关系 应用层 WifiLock 控制 Wi-Fi 芯片电源 WakeLock 控制 CPU 电源 Wi-Fi 芯片 CPU 协同工作:Wi-Fi 不断 + CPU 不睡

从图中可以看到,应用层通过 WifiLock 和 WakeLock 分别控制 Wi-Fi 芯片和 CPU。两者缺一不可,协同工作才能保证后台任务的稳定运行。

14.7 总结

WifiLock 和 WakeLock 是 Android 电源管理的两个重要工具。用好了,你的 App 既稳定又省电;用不好,轻则功能异常,重则被用户投诉耗电。

我个人建议:每次加锁前,先问问自己——这个锁真的需要吗?能不用就不用,能短用就别长用。这才是真正的省电策略。

最后提醒:Android 9 之后,系统对后台应用持锁的限制越来越严。如果你的 App 目标 API 级别较高,建议使用 WorkManagerJobScheduler 来替代手动持锁。这些组件会自动帮你管理电源状态。


公众号:蓝海资料掘金营,微信deep3321