19、功耗优化:合理选择采样频率、在后台时注销监听器、使用WakeLock的正确姿势
距离传感器这东西,平时看着不起眼,功耗却是个隐形杀手。我见过不少项目,明明硬件选型没问题,结果待机电流硬生生多了好几个毫安。一查,原来是距离传感器在后台一直开着,或者采样频率设得太高。说白了,传感器本身功耗不大,但用错了方式,它就能把整个系统的功耗拖垮。
这一章,我跟你聊聊怎么把距离传感器的功耗压到最低。核心就三件事:采样频率怎么选、后台怎么处理监听器、WakeLock怎么用才不坑。
19.1 采样频率:不是越快越好
很多人有个误区,觉得采样频率越高,数据越准。其实不然。距离传感器大多数场景下,只需要检测“靠近”和“远离”两个状态。比如通话时贴耳熄屏,拿开亮屏。这种场景,10Hz 的采样频率完全够用。
我习惯这样选:
- 通话场景:5Hz ~ 10Hz。足够了,人耳贴手机的动作没那么快。
- 手势识别:20Hz ~ 30Hz。比如隔空挥手翻页,需要捕捉快速动作。
- 接近检测(非通话):1Hz ~ 2Hz。比如靠近唤醒屏幕,慢一点没关系。
核心原则:满足业务需求的前提下,采样频率越低越好。每降低一档频率,功耗可能减少 30%~50%。
我在项目中遇到过一件事。有个同事把采样频率设成了 100Hz,理由是“怕丢数据”。结果手机待机时,距离传感器一直在狂采,电流多了 2mA。后来改成 10Hz,功能完全正常,功耗直接降下来了。你想想看,100Hz 和 10Hz,差了 10 倍的数据量,传感器和 CPU 都得跟着忙活。
19.2 后台时注销监听器:别让传感器白干活
应用退到后台,距离传感器还在工作?这绝对是功耗的大坑。我见过不少 App,切到后台后忘了注销监听器,结果传感器一直开着,屏幕都灭了还在采数据。
正确的做法是:
- Activity 或 Fragment 进入 onPause() 时,立即注销监听器。
- 回到前台 onResume() 时,再重新注册。
- 如果后台确实需要检测,比如通话应用,那就用前台服务,并明确告知用户。
代码示例:
@Override
protected void onPause() {
super.onPause();
// 我习惯在这里注销,避免后台空转
sensorManager.unregisterListener(proximityListener);
}
@Override
protected void onResume() {
super.onResume();
// 回到前台再注册,采样频率按需设置
sensorManager.registerListener(proximityListener,
proximitySensor,
SensorManager.SENSOR_DELAY_NORMAL);
}
注意:如果你在 onPause() 里注销了,但 onResume() 里忘了注册,那功能就丢了。我建议写一个工具类,统一管理注册和注销的生命周期,避免遗漏。
嗯,这里要特别提一下。有些开发者喜欢在 onDestroy() 里注销,但 onDestroy() 不一定被调用。比如系统杀进程时,可能直接跳过。所以,onPause() 才是最安全的注销时机。
19.3 WakeLock:用对了是神器,用错了是炸弹
WakeLock 这玩意儿,说白了就是让 CPU 保持清醒的锁。距离传感器本身不需要 WakeLock,但有些场景下,你需要保证传感器数据被及时处理,这时候 WakeLock 就派上用场了。
比如,你在做手势识别,用户挥手时,CPU 不能睡过去。否则传感器数据来了,没人处理。这时候,你需要持有一个 PARTIAL_WAKE_LOCK,让 CPU 保持唤醒。
但问题来了:
- 持锁时间过长:手机无法进入深度休眠,功耗飙升。
- 忘记释放:锁一直挂着,手机永远睡不了。
- 持锁等级不对:用了 FULL_WAKE_LOCK,屏幕都亮了,功耗直接爆炸。
我建议这样用:
- 只持 PARTIAL_WAKE_LOCK,不要用 FULL_WAKE_LOCK 或 SCREEN_DIM_WAKE_LOCK。
- 持锁时间尽量短,处理完数据立即释放。
- 使用 acquire(timeout) 方法,设置超时时间,防止忘记释放。
代码示例:
// 我习惯用超时机制,避免锁死
wakeLock.acquire(3000); // 最多持锁3秒
// 处理传感器数据
processProximityData(event);
// 处理完后立即释放
if (wakeLock.isHeld()) {
wakeLock.release();
}
小技巧:如果你不确定持锁时间,可以用 acquire(timeout) 兜底。即使忘记释放,系统也会在超时后自动释放。这个习惯帮我避免过好几次线上功耗问题。
我曾经在一个项目中,看到同事持了一个 WakeLock 没释放,结果手机一晚上掉了 30% 的电。查了半天,发现是异常分支里忘了 release。从那以后,我所有 WakeLock 都加上了 try-finally 或者超时机制。
19.4 知识体系总览
下面这张图,把功耗优化的三个核心点串起来了。你可以对照着看,哪个环节没做好,功耗就会出问题。
19.5 避坑指南
最后,我总结几个实战中容易踩的坑:
- 采样频率设太高:我曾经见过有人设 200Hz,理由是“怕延迟”。其实距离传感器的物理响应时间就在 10ms 左右,100Hz 已经是极限了。再高,数据全是重复的,白白浪费电。
- 后台不注销监听器:这个坑最常见。尤其是用 Service 的场景,很多人以为 Service 在后台运行,传感器就得一直开着。其实大部分场景下,后台根本不需要距离数据。
- WakeLock 忘记释放:我建议所有 WakeLock 都加上 try-finally 或者 acquire(timeout)。别太相信自己的记忆力,代码跑久了,什么异常都可能出现。
- 持锁等级太高:有些人图省事,直接用了 FULL_WAKE_LOCK。结果屏幕一直亮着,功耗直接翻倍。记住,距离传感器只需要 CPU 醒着,不需要屏幕亮着。
一句话总结:采样频率够用就行,后台记得注销监听器,WakeLock 短持快放。这三件事做好了,距离传感器的功耗基本就压到最低了。
好了,这一章就聊到这儿。功耗优化是个细活,多测多调,慢慢就有感觉了。
公众号:蓝海资料掘金营,微信deep3321