zoukankan      html  css  js  c++  java
  • 通过 Battery Historian 工具分析 Android APP 耗电情况

    电量统计模块概述

    Android 从两个层面统计电量的消耗,分别为 软件排行榜 及 硬件排行榜。它们各有自己的耗电榜单,软件排行榜为机器中每个 App 的耗电榜单,硬件排行榜则为各个硬件的耗电榜单。这两个排行榜的统计是互为独立,互不干扰的。

    具体的说,耗电信息在 设置 -> 电量 中能够非常直观的看到。注意,Android 所有功耗统计都是通过代码估算,没有集成电路参与汇报。准确度取决于厂商 ROM 所提供的 power_profile.xml 文件。由于不同厂商 power_profile.xml 准确度及源码有差异,因此不同手机、不同版本的数据可能有较大差异。

    power_profile.xml 直接影响统计的准确度,并且此文件无法通过应用修改。再次强调,Android 耗电估算没有硬件的参与,全靠代码估算。

    power_profile.xml 文件位于源码下的 /framework/base/core/res/res/xml/power_profile.xml,部分内容展示如下:

      <item name="radio.scanning">0.1</item> <!-- cellular radio scanning for signal, ~10mA -->
      <item name="gps.on">0.1</item> <!-- ~50mA -->
      <!-- Current consumed by the radio at different signal strengths, when paging -->
      <array name="radio.on"> <!-- Strength 0 to BINS-1 -->
          <value>0.2</value> <!-- ~2mA -->
          <value>0.1</value> <!-- ~1mA -->
      </array>
      </array>
      <!-- Different CPU speeds as reported in
           /sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state -->
      <array name="cpu.speeds">
          <value>400000</value> <!-- 400 MHz CPU speed -->
      </array>
      <!-- Current when CPU is idle -->
      <item name="cpu.idle">0.1</item>
      <!-- Current at each CPU speed, as per 'cpu.speeds' -->
      <array name="cpu.active">
          <value>0.1</value>  <!-- ~100mA -->
      </array>
      <array name="wifi.batchedscan"> <!-- mA -->
          <value>.0002</value> <!-- 1-8/hr -->
          <value>.002</value>  <!-- 9-64/hr -->
          <value>.02</value>   <!-- 65-512/hr -->
          <value>.2</value>    <!-- 513-4,096/hr -->
          <value>2</value>    <!-- 4097-/hr -->
      </array>

    这就是在硬件层面统计时,直接参与运算的参数。无论是软件耗电统计还是硬件耗电统计,都通过 BatteryStatsHelper 来进行汇总。BatteryStatsHelper位于 /framework/base/core/java/com/andorid/internal/os/BatteryStatsHelper.java 下。

    软件耗电统计

    在 BatteryStatsHelper.java 中,有这么一个方法:

     
        private void processAppUsage(SparseArray<UserHandle> asUsers) {
            final boolean forAllUsers = (asUsers.get(UserHandle.USER_ALL) != null);
            mStatsPeriod = mTypeBatteryRealtime;
     
            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, mRawRealtime, mRawUptime, mStatsType);
                mWakelockPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
                mMobileRadioPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
                mWifiPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
                mBluetoothPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
                mSensorPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
                mCameraPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
                mFlashlightPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, 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
        }

    processAppUsage()方法中,一个应用的总功耗在这里体现出来了:

    • cpu

    • Wakelock(保持唤醒锁)

    • 无线电(2G/3G/4G)

    • WIFI

    • 蓝牙

    • 传感器

    • 相机

    • 闪光灯

    这些数据,将决定着你的应用在耗电排行榜中的位置,以及是否给予用户警告高耗电。这些警告对于应用来说可能是致命的,用户可能因此而卸载应用。

    应用总功耗是上述八个统计值的和。这八个统计器同继承自 PowerCalculator.java

    具体来说,这八个耗电计算器的算法分别如下:

    耗电统计概述就如上所述。总的来说并不复杂,通过聚合八种不同方式的消耗,来得出总的耗电量,并给予用户展示。

    battery-historian 

    概述

    Battery Historian ,是谷歌出品的耗电分析器。通过 Battery Historian,可将导出的 bugreport 文件可视化。在第一代的 Battery Historian 中,google 使用了 python 作为数据解析工具。拿到 bugreport 文件后,通过终端执行 python 来生成可视化的 html 文件。这种方法使用起来较为麻烦,而且无法部署到服务器。因此在第二代 Battery Historian,google 选择了使用 docker 容器。现在完全不推荐使用第一代 Battery Historian,已经许久没有维护了,而且功能过于简陋。

    需要注意的是 battery-historian 在使用时候不能在充电,同时确保设备运行的 Android 版本是 5.0 及以上。

    通过上面的描述,对 battery-historian 的功能有个大概的了解,下面进入到实战。

    获取 bugreports

    battery-historian 虽然功能强大,但是也是需要先提供数据的,其数据的获取需要我们手动操作。下面介绍如何获取 bugreports。

    • 1. 电脑连接上手机,断开adb服务,adb作为一种连接的方式,有可能被其他的程序占用,所以我们做电量记录时要避免打开很多可能冲突的东西
    adb kill-server
    • 2. 重启adb服务
    adb devices || adb start-server
    • 3. Android也不记录特定于应用程序的用户空间wakelock转换的时间戳。如果您希望Historian在时间线上显示关于每个单独唤醒锁的详细信息,则应在开始实验之前使用以下命令启用完整唤醒锁报告:

    adb shell dumpsys batterystats --enable full-wake-history
    • 4. 采集报告前将battery统计状态重置,重置命令结束后断开usb,测试结束后用获取报告命令导出统计文件包

    adb shell dumpsys batterystats --reset
    • 5. 导出电量,对于 7.0 系统以上的设备运用:
    adb bugreport bugreport.zip
    
    adb bugreport > $HOME/Documents/bugreport.zip  // 指定到对应目录下,具体分机型,可能会有些不一样 

    6.0 或更低版本:

    adb bugreport  >  bugreport.txt

    数据分析

    获取到数据后,接下去就是对数据进行分析了。

    使用 battery-historian 是需要搭建环境的,由于搭建环境比较复杂, 可以借用别人搭好的线上环境:https://bathist.ef.lc/,点击打开后,上传数据,就会得到详细的结果。

    如图所示Battery Historian图表的一个例子:

    其中标号的意义是:

    • 标号1:从下拉列表中添加其他指标;

    • 标号2:将鼠标悬停在信息图标上可以查看有关每个指标的详细信息,包括图表中使用的不同颜色代表意义的介绍;

    • 标号3:将鼠标悬停在某个条目上可以查看该指标的更多详细信息,以及时间线上特定点的耗电量信息;

    这是整个手机状态图,包括手机电量,CPU 使用时长,wifi 信号的强度,手机温度变化等等,都在上面详细的用图表展示出来了。

    Battery Historian除了能够提供宏观的系统层面的信息,还能够提供针对指定App的可视化数据和表格信息,这表格主要信息包括:

    • Device estimated power use等基本信息

    • Networks Information:app网络信息

    • Wakelocks:唤醒锁信息,一般和业务强相关

    • Services:服务信息,查看App开启的services信息

    • Process info:进程信息

    Battery Historian图表下为数据分析,包括三个Tab,如下图所示,可以查看App更多信息:

    其中,标号所代表的意义是:

    • 标号1:System Stats 分组包含系统级别的数据,比如屏幕亮度等。这一栏显示了系统发生的总体情况,可以用来测试是否存在外部影响事件;

    • 标号2:App Stats分组包含针对指定APP的详细信息;

    • 标号3:可以根据不同的分类标准对APP进行排序;

    • 标号4:在下拉列表中选择指定的APP后可在App Stats中查看具体信息,App Stats所展示的都是所选定App产生的数据,不会受到外部因素的影响;

    下面看看具体某个手机的数据,比如百度APP应用的数据,在右边选择对应的 APP,左边就会展示当前 APP 的电量,网络等情况。

      

    bugreport 文件分析

    前面是通过 Battery History 对 bugreport 的文件进行了分析,那如果我们想自己分析呢?因此,在这里有必要了解下 bugreport 的文件内容。

    该功能依次输出内容项, 主要分为5大类:

    1. current log: kernel,system, event, radio;
    2. last log: kernel, system, radio;
    3. vm traces: just now, last ANR, tombstones
    4. dumpsys: all, checkin, app,batterystatus
    5. system info:cpu, memory, io等
    1. 系统build以及运行时长等相关信息;
    2. 内存/CPU/进程等信息;
    3. kernel log
    4. lsof、map及Wait-Channels;
    5. system log
    6. event log
    7. radio log;
    8. vm traces
      1. VM TRACES JUST NOW (/data/anr/traces.txt.bugreport) (抓bugreport时主动触发)
      2. VM TRACES AT LAST ANR (/data/anr/traces.txt) (存在则输出)
      3. TOMBSTONE (/data/tombstones/tombstone_xx) (存在这输出)
    9. network相关信息;
    10. last kernel log;
    11. last system log;
    12. ip相关信息;
    13. 中断向量表
    14. property以及fs等信息
    15. last radio log;
    16. Binder相关信息;
    17. dumpsys all:
    18. dumpsys checkin相关:
      • dumpsys batterystats电池统计;
      • dumpsys meminfo内存
      • dumpsys netstats网络统计;
      • dumpsys procstats进程统计;
      • dumpsys usagestats使用情况;
      • dumpsys package.
    19. dumpsys app相关
      • dumpsys activity;
      • dumpsys activity service all;
      • dumpsys activity provider all.
    • 电量统计信息起始,包含 reset 时间,进程信息等

    • 下面是距离上次充电后的数据统计:

    • 每个 APP 电量使用情况,通过 Uid 来标识APP

     可以通过关键字下面的关键字来寻找相关信息。

    DUMP OF SERVICE    

    通过该关键字可以查到 wifi, 网络, activities 等等的信息。

    Tips: bugreport几乎涵盖整个系统信息,内容非常长,每一个子项都以------ xxx ------开头。 例如APP ACTIVITIES的开头便是 ------ APP ACTIVITIES (dumpsys activity all) ------,其中括号内的便是输出该信息指令,即dumpsys activity all,还有可能是内容所在节点,各个子项目类似的规律

    参考文章

    1、开发者大杀器 —— Battery Historian,刨根问底,揪出 Android App 耗电的元凶代码

    2、Android耗电量 - bugreport & Battery Historian

    树林美丽、幽暗而深邃,但我有诺言尚待实现,还要奔行百里方可沉睡。 -- 罗伯特·弗罗斯特
  • 相关阅读:
    Java实现 蓝桥杯VIP 算法训练 字符删除
    Java实现 蓝桥杯VIP 算法训练 字符删除
    Java实现 蓝桥杯VIP 算法训练 字符删除
    Java实现 蓝桥杯VIP 算法训练 字符删除
    Java实现 蓝桥杯VIP 算法训练 字符删除
    Java实现 蓝桥杯VIP 算法训练 字符串编辑
    Java实现 蓝桥杯VIP 算法训练 字符串编辑
    Java实现 蓝桥杯VIP 算法训练 字符串编辑
    Java实现 蓝桥杯VIP 算法训练 字符串编辑
    Java实现 蓝桥杯VIP 算法训练 字符串编辑
  • 原文地址:https://www.cnblogs.com/huansky/p/14545770.html
Copyright © 2011-2022 走看看