写在前面
前面几篇关于电量的文章,到最后也只是对于一些硬件使用的一些监控,好像没有一个明确的目标或者方向,本以为看完即可对电量优化有提升,但现实并未如此,好像懂了又好像没懂。没懂的有很多,比如怎么定义电量异常消耗,减少电量消耗只知道减少位置信息获取,wifi扫描,传感器减少使用等,...
思考
1.在Android中电量消耗是如何计算的
2.有没有工具可以来衡量电量消耗与使用手机
3.对于电量需要监控哪些维度
4.优化的指标有哪些
1.在Android中电量消耗是如何计算的
电量 = 功率 * 时间 = 电压 * 电流 * 时间 ,这个本人觉得是比较有道理的
在Android系统中有一个power_profile.xml
文件,搜索中对于该文件的描述是“系统的 设置-->电池-->使用情况中,统计的能耗的使用情况也是以power_profile.xml
的value
作为基础参数的”,该文件记录的是手机中的每个部件运行时对于的能耗值,该文件位于/system/framework/framework-res.apk
这个apk
中的res
文件夹下。
该文件的部分信息如下(android9.0代码)
<device name="Android">
<item name="ambient.on">0.1</item> <!-- ~100mA -->
<item name="screen.on">0.1</item> <!-- ~100mA --> // 亮屏时的电流值
<item name="screen.full">0.1</item> <!-- ~100mA -->
<item name="bluetooth.active">0.1</item> <!-- Bluetooth data transfer, ~10mA -->
<item name="bluetooth.on">0.1</item> <!-- Bluetooth on & connectable, but not connected, ~0.1mA -->
<item name="wifi.on">0.1</item> <!-- ~3mA -->
<item name="wifi.active">0.1</item> <!-- WIFI data transfer, ~200mA -->
<item name="wifi.scan">0.1</item> <!-- WIFI network scanning, ~100mA -->
<item name="audio">0.1</item> <!-- ~10mA -->
<item name="video">0.1</item> <!-- ~50mA -->
<item name="camera.flashlight">0.1</item> <!-- Avg. power for camera flash, ~160mA -->
<item name="camera.avg">0.1</item> <!-- Avg. power use of camera in standard usecases, ~550mA -->
<item name="gps.on">0.1</item> <!-- ~50mA -->
...
</device>
知道了该文件,那么对应的电量计算就是 屏幕亮屏一段时间内的电量 = 100mA * 电池电压 * 使用时间,而电池电压在集成到手机内就是不变的,而且比较容易查询到,使用时间获取的话之前文章有提到,是根据接收系统广播来计算(本次息屏广播时间 - 本次亮屏广播时间),其他的硬件电量计算也类似,把所有的电量消耗加起来就可以计算出一段时间内电池电量的消耗值了。
在系统中有一个BatteryStatsHelper.java
类,该类辅助计算电量消耗,而具体的计算方式在各个PowerCalculator
实现类中
public class BatteryStatsHelper {
PowerCalculator mCpuPowerCalculator;
PowerCalculator mWakelockPowerCalculator;
MobileRadioPowerCalculator mMobileRadioPowerCalculator;
PowerCalculator mWifiPowerCalculator;
PowerCalculator mBluetoothPowerCalculator;
PowerCalculator mSensorPowerCalculator;
PowerCalculator mCameraPowerCalculator;
PowerCalculator mFlashlightPowerCalculator;
PowerCalculator mMemoryPowerCalculator;
PowerCalculator mMediaPowerCalculator;
...
private void processAppUsage(SparseArray<UserHandle> asUsers) {
BatterySipper osSipper = null;
final SparseArray<? extends Uid> uidStats = mStats.getUidStats();
final int NU = uidStats.size();
for (int iu = 0; iu < NU; iu++) {
final Uid u = uidStats.valueAt(iu);
final BatterySipper app = new BatterySipper(BatterySipper.DrainType.APP, u, 0);
mCpuPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
mWakelockPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
mMobileRadioPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs,
mStatsType);
mWifiPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
mBluetoothPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs,
mStatsType);
mSensorPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
mCameraPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
mFlashlightPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs,
mStatsType);
mMediaPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
final double totalPower = app.sumPower();
if (DEBUG && totalPower != 0) {
Log.d(TAG, String.format("UID %d: total power=%s", u.getUid(),
makemAh(totalPower)));
}
}
}
}
public class CameraPowerCalculator extends PowerCalculator {
private final double mCameraPowerOnAvg;
public CameraPowerCalculator(PowerProfile profile) {
mCameraPowerOnAvg = profile.getAveragePower(PowerProfile.POWER_CAMERA);
}
@Override
public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
long rawUptimeUs, int statsType) {
// Calculate camera power usage. Right now, this is a (very) rough estimate based on the
// average power usage for a typical camera application.
final BatteryStats.Timer timer = u.getCameraTurnedOnTimer();
if (timer != null) {
final long totalTime = timer.getTotalTimeLocked(rawRealtimeUs, statsType) / 1000;
app.cameraTimeMs = totalTime;
app.cameraPowerMah = (totalTime * mCameraPowerOnAvg) / (1000*60*60);
} else {
app.cameraTimeMs = 0;
app.cameraPowerMah = 0;
}
}
}
总结:
1.Android 各个硬件模块的消耗功率在power_profile.xml
中
2.计算消耗电量方式为:功率 * 时间 = 电压 * 电流 * 时间
2.Battery Historian 检测工具
这个工具尝试安装没有安装成功,之后又试了线上的网站分析工具https://bathist.ef.lc/
结果502...,就很无奈,只能看着文章网上的文章来写这部分。
Battery Historian
是在 Android 5.0 Lollipop(API 级别21)及更高版本的 Android 设备上检测与电池相关的信息和事件,而在此期间,该设备没有插上电源。它允许应用程序开发人员在时间轴上可视化系统和应用级别的事件,并使用平移和缩放功能,在设备最后一次完全充电之后,可以轻松地查看各种聚合统计信息,可以选择一个应用程序,检查所选择的应用程序对电池指标的影响。此外,它还允许对两个错误报告进行 A/B 比较,突出显示了关键电池相关指标的差异。
简言之就是可以记录下一段时间内手机的状态和电量消耗情况即手机哪个部分干了什么导致电量消耗的工具,记录的内容有息亮屏,CPU运行,内核,GPS 运行,WIFI、蓝牙扫描,电池低电量等等的状态。
使用步骤:
1.在检测手机电量前将之前的信息清空:
adb shell dumpsys batterystats --reset
-
2.开始记录数据:
adb shell dumpsys batterystats --enable full-wake-history
- 分为亮屏时操作app(WIFI,位置,相机,音乐或视频格外注意)和息屏一段时间内的后台测试(看看有哪些不合理的动作出现)
3.拉取记录的数据:
adb shell dempsys batterystats > 文件保存路径/文件名
4.将文件上传到自己搭建或线上平台开始分析,类似与下图
图中的数据就不分析了,参考这篇文章(毕竟图都是这里偷的)电量分析工具 Battery Historian 的配置及使用
该工具可以很好的表现出电量消耗的原因在哪里,适合在上线前检测代码有哪里写的不规范
3.对于电量需要监控哪些维度
结合matrix
中的内容,监控电量的维度有那么几方面
1.在测前台的时候不要忘记运行在后台的检测
2.记录下硬件模块的调用(亮息屏,WIFI、蓝牙扫描,获取定位信息,闹钟,是否移动网等)
3.后台工作调度 :Job
、Alarms
、WorkManager
、WakeLock
等
而该额外记录的信息应该有堆栈信息,是否充电,是否处于低电量,前后台时间,CPU状态等
4.优化的指标
优化的思路应该是减少不必要的硬件模块的调用,在息屏时减少不必要的后台运行,将后台运行合并延迟到一个合适的时机
1.优化后台耗电,减少使用不必要的WakeLock
、获取位置、WIFI
和蓝牙扫描等
2.传输大数据时尽量在充电或者WIFI
情况下
3.定位选择低精度,网络定位代替GPS
,减少频率
4.在进入后台时,app主动释放一些与界面有关的东西,如动画等
5.减少使用硬件传感器的频率
写在最后
其实 Android 在最近的版本中对于后台的限制也越来越严苛,在 Android O 中限制了后台执行和后台位置及更多的优化了省电功能,在 Android P 中提出应该待机分组(按 app 的使用将 app 分为活跃组-正在使用、工作组-应该频率很高、常用组-经常但不是每天用及少用组-偶尔使用,对于不同分组的 app 有不一样的限制使用方法),更加严格的 app 后台使用限制及省电模式。