之前文章里虽然说技改难做不好做,但出于绩效上的压力,还是绞尽脑汁给自己想了几个技改需求,其中之一就是准备把项目当中的一些功耗问题解决一下,在此之前,我也整理了一些比较常见的,会造成功耗问题的行为,可以事先看下,预习预习
WakeLock未及时释放
这个就是应用获取了 PARTIAL_WAKE_LOCK 但忘记释放,导致 CPU 无法进入休眠状态,系统持续耗电

就像上面这样,只调用了acquire但没有调用release,或者是出现异常了,没有走到release方法里面,相当于一直在执行耗时任务,所以遇到这种情况,我们要么加上超时时间,要么加上try-catch,保证release肯定被走到

网络请求无节流
有的应用为了确保数据的实时性,会在后台频繁发起网络请求,没有任何退避策略

就像上面这样,尽管有加间隔,但还是请求过于频繁,解决方法是用WorkManager来处理这类周期性任务,把优化工作交给系统来做

位置服务滥用
- 使用
GPS_PROVIDER持续高精度定位 - 定位回调后未取消监听
minTime参数设得太小

上面这段代码就比较吓人了,距离为0的时候,不停的在请求定位数据,那机子不烫手才怪,正确做法应该是根据场景来选择合理精度,不是一些关键场景,精度可以略微粗糙点,谁会在乎这些呢是不

然后设置合理的间隔时间跟距离,监听器用完了记得注销

可以的话优先使用系统级别的FusedLocationProviderClient(不过一般国内很少用这个,懂得都懂)

后台Service常驻
一些应用可能会在后台设置了长期的Service一直在跑着,然后又通过START_STICKY来使得服务就算被杀了,也还能重启

这样子也是很耗电的行为,那应该怎么做呢?如果是短期任务,推荐使用协程,并在完成任务后主动停止任务

如果是延迟任务,则推荐使用WorkManager

AlarmManager频繁唤醒
使用 AlarmManager.RTC_WAKEUP频繁唤醒设备,并且唤醒间隔比较短,如果设备中多个app都使用了AlarmManager,唤醒设备的次数增加,CPU不断被重启,导致电量消耗快,机子发热等现象

上述问题中,针对多个app都在用AlarmManager去唤醒设备的问题,正确的做法应该是合并闹钟,统一调度

一些场景如果需要延后执行的,用set方法来取代setRepeating方法

如果唤醒需求不是很高的场景,少用RTC_WAKEUP,多用RTC,两者的区别是前者会强行唤醒设备,设备会"生气上火",而后者只有在设备自然唤醒时候才会触发,比较温柔,设备不会"生气上火"

隐式/静态广播滥用
有的应用会频繁的监听系统发送的广播,如ACTION_SCREEN_ON/OFF、ACTION_TIME_TICK、CONNECTIVITY_CHANGE,或者一些高频的广播使用静态注册,这样也是会产生功耗问题

这种广播应当只有在用到的时候才去注册它,并且在不用的时候立即释放

Sensor 未注销
这个也是比较经典的问题,传感器注册了,但是却没有在适当的位置注销,导致传感器芯片一直在工作

正确的做法当然是在不用的时候尽快注销传感器

其中不同采样率的功耗差异如下所示
| 采样率 | 回调间隔 | 适用场景 |
|---|---|---|
SENSOR_DELAY_FASTEST |
0ms | 游戏、手势 |
SENSOR_DELAY_GAME |
~20ms | 游戏 |
SENSOR_DELAY_UI |
~60ms | UI 更新 |
SENSOR_DELAY_NORMAL |
~200ms | 计步器、方向 |
频繁GC与内存抖动
短时间内大量创建和销毁对象,导致 ART/Dalvik 频繁触发 GC,GC 本身是 CPU 密集型操作,会阻止 CPU 进入低功耗状态,持续耗电,典型场景就是在绘图的时候创建对象

这种问题基本都会解决,一个对象能复用就复用,不要频繁去创建

这里问题来了,为啥频繁GC会加剧耗电呢?主要是多次GC就意味着CPU需要多次切状态,从空闲状态切换着活跃状态,人家一直没闲着,一直在工作,肯定热啊
Handler / Timer 循环任务未清理
这类问题主要存在于Activity或者Fragment销毁后,Handler消息仍在排队或Timer仍在执行,导致CPU空转

此类问题的处理方式思路差不多,用完了及时清理,将消息以及注册的回调都移除掉

WorkManager 配置不合理
有些项目虽然用了WorkManager,但是由于配置不合理,比如将非紧急周期性任务的间隔时间设置的太短,或者没有设置任何的约束条件,任务中做了大量无用的计算,也是会导致一些功耗问题产生

正确的做法应该是
延长周期并添加约束

合并同类型 WorkRequest,避免多个 Worker 各自运行

蓝牙 / WiFi 扫描不停止
这种跟上面传感器,广播的问题类似,也是用了之后没有调用stopScan方法,导致一直再扫描周边蓝牙设备或者wifi热点

解决方法的思路也是一样的,设置超时时间,并且用完之后要及时停止扫描

另外还可以配置ScanFilter来过滤掉不相关的设备,只扫描符合条件的设备,避免应用频繁被唤醒处理无用的扫描结果

ContentObserver滥用
又是一个类似的问题,有些应用注册来 ContentObserver 监听系统数据库(如短信、联系人、媒体库)变化,但未在不需要时取消,导致每次变化都触发回调造成 CPU 唤醒,这种问题的解决方法也是一样的,注册与注销两个动作相呼应就行

动画未及时取消
这种问题主要表现在
- 页面退出后属性动画仍在运行
ViewPropertyAnimator未设置withEndAction做清理AnimatorSet无限循环

我们应当在不使用动画或者使用完动画后,及时暂停或者取消动画,防止不必要的电量损失

或者使用View自带的属性动画,在动画结束后将View隐藏掉

总结
大致的可以发现,多数行为都是围绕在用完以后未及时关闭回收上,这种问题都是比较好解决,并且风险都比较小,而有些牵扯到要改框架,改架构的,就必须好好规划一下,协调好测试资源才行,毕竟这类问题多数都围绕在核心业务里面,如果因为一个技改导致业务上出了问题,那可真的是费力不讨好了