五. APM 耗电监控建设
影响App电量的因素有: 有屏幕, GPS,CPU,Radio,Video/Audio,Wifi 或蓝牙等
- 那么我们该如何计算耗电量呢?
- 怎么监控多进程,前后台等不同方式监测耗电情况呢?
手机系统耗电这块我们可以根据 /system/framework/framework-res.apk 的 power_profile.xml--文件进行耗电估算,里面的 value 值代表着不同类型设备发送不同动作的耗电系数: 如下图所示:
包括: BlueTooth , Wifi, Radio ,CPU 等
手机的系统耗电量 = CPU 毫秒数* 系数 1 + 流量 Bytes*系数 2 +....,当然不同类型参数是不一样的,应用也无法获取电量的具体细节
我们可以看一下 com.android.internal.os.BatteryStatsHelper 的关键方法 processAppUsage 里面按照 不同类型分别估算了不同服务耗电情况
private void processAppUsage(SparseArray<UserHandle> asUsers) { final boolean forAllUsers = (asUsers.get(UserHandle.USER_ALL) != null); mStatsPeriod = mTypeBatteryRealtimeUs; 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))); } // ----code------ }
至此: 我们也总结出影响 App 耗电的模块有如下几种情况
APM 耗电监控的关键就是需要定义全局参数估算 Location 时间, Alarm 次数,Net 访问量,wake_lock 持有时间,CPU 耗电情况等等~
耗电监控最大的难度在于:怎样通过 Hook wake_lock 持有长和 alarm 阀值 来预判 alarm 是否在做定时的重复任务,怎样通过 Hook 代理 LOCATION_SERVICE 实现 GPS 监控 ;
怎样通过 Hook 传感器的 SENSOR_SERVICE 中的“mSensorListeners”,拿到部分信息,最后才是通过埋点方案,在申请资源的时候将堆栈信息保存起来。当我们触发某个规则上报问题的时候,可以将收集到的堆栈信息、电池是否充电、电池的健康状态,CPU 信息、应用前后台时间等辅助信息也一起带上实现
整个耗电监控架构图如下: 利用多进程收集信息然后再传递给主进程,然后对耗电进行评估,最后再决定是否上报
那么我们怎么去监测手机的电量变化情况呢?
最核心的是通过广播来实现,在 BatterMannager 有六个比较核心的字段
字段 | 含义 |
STATUS_CHARGING | 表示充电状态 |
STATUS_DISCHARGING | 放电中 |
STATUS_NOT_CHARGING | 未充电 |
STATUS_FULL | 电池满 |
BATTERY_PLUGGED_AC | 表示充电类型 |
BATTERY_PLUGGED_USB | 表示 USB |
电池的健康状态 也有一些七个比较核心的参数,他们返回一个 code, 这里我们也可以看一下:
字段 | 含义 |
BATTERY_HEALTH_UNKNOWN | 未知 |
BATTERY_HEALTH_GOOD | 良好 |
BATTERY_HEALTH_OVERHEA | 过热 |
BATTERY_HEALTH_DEAD | 没电 |
BATTERY_HEALTH_OVER_VOLTAGE | 过电压 |
BATTERY_HEALTH_UNSPECIFIED_FAILURE | 未知错误 |
BATTERY_HEALTH_COLD | 过冷 |
基于这几点我们可以大胆预设做一套符合企业规则的耗电 APM 体系,
监控耗电本身也会带来更多耗电,那么我们该如何持续监控电池电量变化,其实我们真实环境是我们得找到低电量状态,如果手机电量过低自动关机,电池温度异常状态下监控并将数据上报,APM 管理系统可进行统计分析后,再通知手机做出电量补救措施,那么怎么看手机充电状态呢?可以参考以下代码
IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); Intent batteryStatusIntent = registerReceiver(null, ifilter); //如果设备正在充电,可以提取当前的充电状态和充电方式(无论是通过 USB 还是交流充电器),如下所示: // Are we charging / charged? int status = batteryStatusIntent.getIntExtra(BatteryManager.EXTRA_STATUS, -1); boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING || status == BatteryManager.BATTERY_STATUS_FULL; // How are we charging? int chargePlug = batteryStatusIntent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1); boolean usbCharge = chargePlug == BatteryManager.BATTERY_PLUGGED_USB; boolean acCharge = chargePlug == BatteryManager.BATTERY_PLUGGED_AC; if (isCharging) { if (usbCharge) { Toast.makeText(MainActivity.this, "手机正处于USB连接!", Toast.LENGTH_SHORT).show(); } else if (acCharge) { Toast.makeText(MainActivity.this, "手机通过电源充电中!", Toast.LENGTH_SHORT).show(); } } else { Toast.makeText(MainActivity.this, "手机未连接USB线!", Toast.LENGTH_SHORT).show(); }
关于 Hook 方案就不多做介绍了,java 层一般使用动态代理加静态代理方案实现,其他插桩方式如: ASM,javasisit,AspectJ 亦可,native 可以考虑使用 weishu 的 arthoook 替换 native 方法,这个可以结合我的 Github 项目 BatteryCanary 进行代码分析
因为 在 Android P 之后,很多的 Hook 点都不支持了。 所以 APM 耗电监控变得异常艰辛曲折~
六. 总结
本文主要是通过我业余时间的技术调研,利用线下工具 Battery Historian 分析企业 app 电池发热问题,电池的信息,电压,温度,充电状态,Device estimated power use 这几个指标是线下测试的关键指标
对于 Location 时间, Alarm 次数,Net 访问量,wake_lock 持有时间,CPU 耗电情况等等估算,我们可以埋点采集,指标采集核心思想还是 Hook 方案, APM 耗电建设是一个费力不讨好的工程,耗电监控相关代码我也从张绍文的 Matrix 仓库抽出来并进行源码分析注释,