更多精彩内容,欢迎观看:
钉钉 Android 端功耗优化最佳实践(上):https://developer.leiyu.cn/article/1262699?spm=a2c6h.13148508.setting.14.1fb44f0e1zEMZJ
感知能力的建设对于了解线上功耗健康度非常关键,对于我们甄别头部问题以及防劣化体系意义重大。帮助我们从被动应对转变为主动出击。
首先简单说明下 Android 系统是如何统计耗电量的。物理学中电量计算公式:
电量 = 功率 × 时间 = (电压 × 电流 )× 时间
手机上的电压一般是恒定不变的,所以计算电量可直接使用电流来代替功率;再结合各个硬件模块在不同状态下的使用时间,则可以计算出其消耗的电量。
系统服务 BatteryStatsService 就是用于耗电量计算。负责电池信息收集,以及各个部件、各个应用程序的各类别的耗电量统计。计算电池剩余使用时间,电池充满时间等。
系统统计电量的流程是这样的:Android 系统将各个硬件模块的电流消耗值以及该模块在一段时间内大概消耗的电量以固定值的形式存储在 power_profile.xml(电源配置文件)中。由于硬件之间的差异,电源配置文件需要各个设备制造商进行定制。PowerProfile 负责解析电源配置文件,获取各个功耗部件的功耗值,并将获取的值提供给 BatteryStatsService。BatteryStatsService 则会委托 BatteryStatsImpl 跟踪统计各个硬件模块的状态和使用的时间,通过 BatteryStatsHelper 交给各个硬件模块的 PowerCalculator 计算模块的电量,以此来估算 App 整体耗电量。
通过系统的电量统计原理了解到系统的耗电量统计与哪些组件的哪些行为有关,以及统计流程和方法。在无法直接获取耗电量情况下,可以参照系统的统计原理,监控耗电相关的组件的使用情况,以此来统计功耗使用数据、反映功耗消耗情况。
根据系统电量统计原理,结合异常耗电的基线标准,以及钉钉的业务情况,我们主要监控以下模块的使用情况:
接下来将介绍下各个功耗部件的监控方案。
在前文中已经介绍过,Mobile Radio 和 WiFi 模块的耗电不仅仅与流量大小相关,还与网络状态激活的次数和间隔相关。频繁的连续的网络收发非常影响耗电。所以网络部分主要监控:①流量、②网络收发事件、③网络变化数 3 个指标。
流量的统计,Android 10 以上主要是利用 TrafficStats 的 getUidRxBytes 和 getUidTxBytes 获取接收和发送的字节,利用 getUidRxPackets 和 getUidTxPackets 获取接收和发送的数据包数量,结合 App 当前前后台状态、WiFi /移动网络连接状态,计算出流量的消耗。Android 10 以下则利用 /proc/net/xt_qtaguid/stats 获取不同类型网络数据,结合 App 当前前后台状态,计算流量消耗。
再通过钉钉统一网络服务统计上下行网络请求记录事件,以及计算网络变化数和最大连续网络变化数。通过网络变化数和最大连续网络变化数,可有助于分析网络的使用频率;网络收发事件则有助于定位原因。
功耗相关的系统服务调用包含:WakeLock、Alarm、蓝牙扫描、WiFi 扫描、Location、Senser 等的使用。根据系统电量统计原理,监控这些服务与功耗相关的事件的调用,输出事件日志,详细的堆栈信息等。
系统服务调用的监控,主要采用 Java Hook 的方式来实现。但是,Hook 系统服务调用在不同 Android 版本上会存在一些的兼容性问题,需要做好适配工作。另外,参考系统相关原理,为了让功耗监控更准确,有些需要注意的细节:
CPU 使用监控主要是针对 CPU 长期高负荷、过于繁忙的场景,需要监控 CPU 使用率这个重要指标,主要包含:
proc/[pid]/stat
:该文件包含了某一进程所有的活动的信息,该文件中的所有值都是从系统启动开始累计。该文件中的 pid 字段表示进程号。proc/[pid]/task/[tid]/stat
该文件包含了进程下所有的活动的信息,该文件中的所有值都是从系统启动开始累计。该文件中的 tid 字段表示线程号。proc/[pid]/stat
,解析其中的 utime / stime / cutime / cstime , 将其和(utime + stime + cutime + cstime) 与上一次采样时的和做差,这就是这一段时间内该进程占用 CPU 的时间,单位为 TICK 。而总的 CPU 时间为 period * HZ。所以,进程的 CPU 使用率可以用如下公式计算:((utime + stime + cutime + cstime)- (lastutime + laststime + lastcutime + lastcstime)) / period * HZproc/[pid]/task/
统计的线程开销则还需要进一步拆分定位,每一个任务的执行开销是多少。部分手机的耗电详情上统计了应用的自启动次数,鉴于此,主要监控项为:①自启动次数;②自启动原因;③进程近期退出原因。
功耗消耗是一个过程,是一段时间累积的结果。在一段时间当中,应用可能会在前台/后台等多种状态之间切换,设备可能在充电不充电之间切换、亮灭屏之间切换,而异常耗电更多的是关注在后台、并且是不充电的情况下,忽略状态信息可能会导致许多误报的异常功耗问题。所以,在功耗部件使用监控的基础上,还要记录每一次的状态变化事件;将统计窗口内的状态变化,转变为这段时间内每一种状态的时长占比。在分析功耗问题的时候,将上述功耗模块的使用情况结合这一段时间内应用/设备状态的占比信息,就能更准确地定位功耗问题。
这部分监控包含:应用状态;设备状态;电池信息等。
在对功耗部件使用情况具备监控情况下,接下来就需要对超过阈值的使用情况认定为异常耗电,异常耗电的监控对于主动感知异常耗电问题至关重要。
我们参考 Android Vitals 的功耗性能指标和手机系统的异常耗电提醒类型制定钉钉异常耗电规则以及实时诊断感知:
耗电类型 | 监控部件 | 耗电原因 |
后台网络使用量过高 | 网络流量 | 退后台网络流量高 |
后台网络使用频繁 | 网络事件 | 退后台频繁唤醒网络 |
后台持锁时间过长 | WakeLock | 退后台长期持有锁不释放 |
后台频繁唤醒 | Alarm | 退后台频繁唤醒 |
后台蓝牙持续扫描 | 蓝牙扫描 | 退后台频繁扫描蓝牙 |
后台 WiFi 频繁扫描 | WiFi 扫描 | 退后台频繁扫描 WiFi |
后台频繁自启动 | 自启动 | 退后台应用频繁自启动 |
后台频繁定位 | Location | 退后台灭屏长时间使用 GPS /网络定位 |
后台 CPU 负载过高 | CPU | 退后台有长耗时线程,线程死循环 |
... | ... | ... |
基于这套异常耗电诊断模型,我们能有效感知线上异常高耗电问题。监控上线后,帮我们监控到钉钉潜在的功耗问题。
功耗部件异常监控占比分布,便于洞察功耗头部问题;
单个功耗部件异常功耗的主要归因。例如,下图展示后台长时间持锁的主要归因分布。
基于感知能力的功耗部件监控以及使用统计日志,最重要的功耗数据产物之一就是:电量报告。
电量报告会显示一段时间各个功耗部件的使用情况。根据电量报告,就可快速定位这个时间窗口内最主要的电量消耗,再结合电量事件日志,就能准确定位问题了。
更多精彩内容,欢迎观看:
钉钉 Android 端功耗优化最佳实践(下):https://developer.leiyu.cn/article/1262696?groupCode=alibabaf2e
Copyright © 2023 leiyu.cn. All Rights Reserved. 磊宇云计算 版权所有 许可证编号:B1-20233142/B2-20230630 山东磊宇云计算有限公司 鲁ICP备2020045424号
磊宇云计算致力于以最 “绿色节能” 的方式,让每一位上云的客户成为全球绿色节能和降低碳排放的贡献者