史上最全!Android程序性能优化之耗电优化(一)
2024-02-27 14:04:18发布 浏览393次 信息编号:100407
友情提醒:凡是以各种理由向你收取费用,均有骗子嫌疑,请提高警惕,不要轻易支付。
史上最全!Android程序性能优化之耗电优化(一)
概念:什么是功耗优化?
有的同学可能会疑惑,所谓的功耗优化不就是降低应用的功耗,增加用户的续航时间吗? 但在实践中,如果我们的应用程序需要播放视频、获取 GPS 信息或拍照,这些功耗似乎是不可避免的。
如何判断哪些功耗可以避免或者需要优化? 你可以看看下面的图片。 当用户查看耗电量排行榜时,发现《王者荣耀》已经使用了7个多小时。 此时用户对于《王者荣耀》的耗电量是有预期的。
假设这个时候他发现有一个应用程序根本没有使用(前台时间很少),但是耗电很大。 这种情况会和用户的期望有很大的出入,他可能想要抱怨。
因此,功耗优化的第一个方向就是优化应用的后台功耗。
知道了系统是如何计算功耗的,反过来我们也可以知道哪些应用程序不应该在后台做,比如长时间采集、WiFi和蓝牙扫描等。为什么说功耗优化第一方向是优化应用后台功耗? 这是因为大多数厂商的预装项目最严格的要求就是应用程序的后台待机功耗。
当然,我们不会完全忽视前台的耗电量,但标准会放宽很多。看下图。 如果系统对你的应用弹出这个对话框,对于微信用户或许还可以忍受,但对于大多数其他应用,很多用户可能就直接加你了。 它在后台限制列表中
功耗优化的第二个方向是遵守系统的规则,让系统认为你的功耗是正常的。
P监控后台功耗,所以我们需要遵守规则。 目前,其具体规则如下:
虽然上述标准随时可能发生变化,但可以看出系统目前更关心的是后台闹钟唤醒、后台网络、后台WiFi扫描以及一些长期防止系统后台休眠的问题。
1. 功耗监控
功耗监控也是如此。 我们首先需要抽象出具体的规则,然后收集尽可能多的辅助信息来帮助解决问题。
1.
我们先回顾一下一些电源监控方案和规则。
使用了一段时间后,我发现它并不是那么好用。 以闹钟为例,规则是每小时10次以上。 由于这个规则无法修改,很多时候我们可能希望对不同的系统版本进行更详细的区分。
其次,和之前一样,我们只能获取标记的组件,但无法获取申请的堆栈,也无法获取手机当时是否在充电、剩余电量等信息。
网络、WiFi 扫描等也是如此。 虽然帮助我们缩小了排查范围,但我们仍然需要在浩瀚的代码中找到对应的可疑代码。
2、功耗监控监控什么?
不太好用,对于国内应用来说实际上根本用不着。 不管怎样,我们还是需要建立自己的用电监控系统。
那么我们的用电监控系统应该监控哪些内容,怎样才能做得更好呢?
在绿色联盟会议上,华为公开了其后台资源使用的“红线”。 您还可以参考其中的一些规则:
3. 如何监控电量消耗
明确了我们需要监控的内容以及具体规则后,终于可以来到实现步骤了。像I/O监控、网络监控,我首先想到的就是Hook方案
Java钩子
Hook方案的优点是用户访问非常简单,不需要修改自己的代码。 下面我就以几个常用的规则为例来看看如何使用Java Hook来达到监控的目的。
// 代理 PowerManagerService
ProxyHook().proxyHook(context.getSystemService(Context.POWER_SERVICE), "mService", this);
@Override
public void beforeInvoke(Method method, Object[] args) {
// 申请 Wakelock
if (method.getName().equals("acquireWakeLock")) {
if (isAppBackground()) {
// 应用后台逻辑,获取应用堆栈等等
} else {
// 应用前台逻辑,获取应用堆栈等等
}
// 释放 Wakelock
} else if (method.getName().equals("releaseWakeLock")) {
// 释放的逻辑
}
}
// 代理 AlarmManagerService
new ProxyHook().proxyHook(context.getSystemService
(Context.ALARM_SERVICE), "mService", this);
public void beforeInvoke(Method method, Object[] args) {
// 设置 Alarm
if (method.getName().equals("set")) {
// 不同版本参数类型的适配,获取应用堆栈等等
// 清除 Alarm
} else if (method.getName().equals("remove")) {
// 清除的逻辑
}
}
通过Hook,我们可以保存申请资源时的堆栈信息。 当我们触发规则报告问题时,我们还可以带上收集到的辅助信息,例如堆栈信息、电池是否充电、CPU信息、应用程序前后时间等。
虽然使用 Hook 非常简单,但有些规则可能会导致很难找到合适的 Hook 点。 而且P之后,很多Hook点就不再支持了。
出于兼容性的考虑,我首先想到的是写一个基础类,然后在统一的调用接口中添加监控逻辑。 例如:
public class WakelockMetrics {
// Wakelock 申请
public void acquire(PowerManager.WakeLock wakelock) {
wakeLock.acquire();
// 在这里增加 Wakelock 申请监控逻辑
}
// Wakelock 释放
public void release(PowerManager.WakeLock wakelock, int flags) {
wakelock.release();
// 在这里增加 Wakelock 释放监控逻辑
}
}
还有一个用于功耗监控的开源库——它监控的数据非常全面,包括Alarm、、、CPU等,还收集电源充电状态和电量等信息。
- 只提供一系列基础类。 在实际使用中,用户可能需要修改大量的源代码。 但对于某些第三方SDK或后续添加的代码,我们可能无法保证可以监控。 这些场景无法监控,因此内部使用来动态替换它们。
不幸的是,他们的内部仪器没有开源实现。 然而,这并不难实施。 事实上,在我们之前的工作中,我们已经使用过ASM和这两种仪表解决方案。 稍后我也会单独安排一期来谈谈不同仪器方案的实现。
仪器仪表方案兼容性非常好,用户接入成本并不高。 但它并不完美,不能被系统的代码插桩解决方案取代,比如应用场景。
总结
从系统计算功耗的方法中,我们知道哪些模块需要关注其功耗。 从功耗优化的演进中,我们知道了功耗优化的一些方向和关注点。 从功耗监控中,我们知道了功耗优化的监控方法。
然而,系统方法可能并不完全适合我们的应用。 我们还是需要通过进一步阅读源码和思考,沉淀出一套自己的优化实践方案。 这也是我的性能优化方法论,在其他领域也是一样的。
提醒:请联系我时一定说明是从星空网上看到的!