欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android 耗电总结---概览

程序员文章站 2022-05-04 07:51:40
...

引言

之前和业务线进行耗电优化,业务线是 IoT 设备,故对耗电的要求很高,多一次闹钟唤醒,多一次网络请求都无所遁形。再加上之前适配 Android O 对 background Service 的限制,感觉耗电问题的知识过于零散,于是就想总结一下,本文的主要的目标是想让大家有一个总体的印象,了解 Android 是如何计算耗电,后面会陆续对 Android 的耗电进行总结。

耗电计算

分析耗电之前,我们首先要明白 Android 是如何统计耗电的。耗电的统计在类

frameworks/core/java/com/android/internal/os/BatteryStatsHelper.java
其中耗电分为 硬件耗电和 app 耗电,硬件耗电统计的是这段时间内手机不同硬件的耗电情况,app 的耗电统计的是这段时间内 app 的耗电,开发过程中涉及到的最直接的耗电是 app 耗电,但是还是先了解一下硬件耗电。

硬件耗电

一说到耗电,我们就会想到初中的物理公式

P = U * I	
W = P * t
其中 P 表示功率,U 表示电压,I 表示电流,也就是说功率是电压和电流的乘积;功耗 W 是功率 P 和时间的乘积。

其实 Android 设备的功率并没有我们想象的那么复杂,由于 Android 设备电压恒定,只需要定义每种硬件设备在运行时的电流即可计算一段时间内每种硬件的功耗。Android 将每种硬件在运行时的电流保存以下文件中

frameworks/core/res/res/xml/power_profile.xml
可以看下部分配置信息:
<device name="Android">	
  ......	
<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 -->	
  .......	
</device>

可以看到以上的配置文件中,定义了 Wi-Fi 模块在 on active 以及 scan 时不同的电流数值。通过这些预定义的数据再加上运行的时间,就可以知道这段时间内不同硬件的耗电情况。

以上就是 Android 统计硬件耗电的方法,总结起来就是先定义好不同硬件在不同状态下的功率,然后根据时间计算这段时间内硬件的耗电,感兴趣的同学可以去仔细分析一下 BatteryStatHelper 类中 processMiscUsage 函数。

软件耗电

app 耗电的计算实际上就是计算 app 在运行期间使用手机硬件所产生的能耗。可以看下计算的逻辑。

private void processAppUsage(SparseArray<UserHandle> asUsers) {	
    ......	
    for(int iu = 0; iu < NU; iu++) {	
        // 统计每一个应用的耗电信息	
        final Uid u = uidStats.valueAt(iu);	
        final BatterySipper app = new BatterySipper(BatterySipper.DrainType.APP, u, 0);	
        // CPU	
        mCpuPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);	
        // WakeLock	
        mWakelockPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);	
        // 移动网络	
        mMobileRadioPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs,	
                mStatsType);	
        // wifi	
        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();	
        ......	
    }	
    ......	
}
这里,可以理解 uid 对应于一个 app ,上面的注释已经很清楚了,在执行最终的 app.sumPower() 执行之前,需要分别计算 CPU,wakeLock,移动网络,wifi,蓝牙,传感器,闪光灯以及多媒体的耗电。这里不会去具体分析每种硬件的耗电,在后面会逐步穿插进行分析,总之,大家需要提前在脑海中植入一个概念:Android 软件的耗电其实就是软件使用的硬件耗电,耗电最终的决定因素是使用时间,有了这样的概念之后,如果被测试同学发现了功耗问题,首先就要去分析使用了耗电相关的接口后,是否需要进行释放以及是否进行了释放。

可以看下 sumPower( ) 函数的实现

public double sumPower() {	
    totalPowerMah = usagePowerMah + wifiPowerMah + gpsPowerMah + cpuPowerMah +	
            sensorPowerMah + mobileRadioPowerMah + wakeLockPowerMah + cameraPowerMah +	
            flashlightPowerMah + bluetoothPowerMah + audioPowerMah + videoPowerMah;	
    ......	
    return totalPowerMah;	
}
sumPower( ) 函数的逻辑比较简单,将 processAppUsage 计算的结果进行合并,就得到了 totalPowerMah。注意最后的 audioPowerMah + videoPowerMah 就是 processAppUsage 中计算的多媒体耗电。

总结

本文带着大家了解了 Android 耗电计算的逻辑,后面的文章将会带着大家逐步去分析 Android 的 App 耗电。

推荐阅读