1. 项目缘起:为什么需要从ADB层面操作电池信息?
在Android应用开发或者设备测试的日常工作中,我们经常会遇到一些与设备电量相关的棘手场景。比如,你正在开发一个需要深度优化功耗的App,或者在进行自动化测试时,需要模拟设备在不同电量下的行为(例如,低电量告警、省电模式触发)。如果仅仅依赖应用层API,你会发现限制颇多:很多底层的、实时的电池状态信息无法获取,更别提去主动设置或模拟某些状态了。
这时,Android Debug Bridge,也就是我们常说的ADB,就成为了一个强大的“后门”。它允许我们绕过应用层的限制,直接与Android系统的底层服务进行交互。通过ADB获取电池信息,我们能拿到比BatteryManagerAPI更原始、更丰富的数据;而“设置”电池信息的能力,则主要服务于测试场景,比如在自动化测试框架中,伪造一个“电量即将耗尽”的状态,来验证应用的降级逻辑是否正常触发。
简单来说,这个技能点属于“平时用不到,用到就是宝”的类型。它连接了应用开发、系统测试和性能调优等多个环节。接下来,我就结合自己踩过的坑和实际项目经验,带你从零开始,彻底搞懂如何通过ADB玩转电池信息。
2. 环境准备与核心工具链梳理
在开始实操之前,确保你的“武器库”是齐全且配置正确的。很多新手卡在第一步,问题都出在环境上。
2.1 ADB工具的正确安装与连接验证
首先,你需要ADB工具。它包含在Android SDK Platform-Tools中。最稳妥的方式是去Android开发者官网下载独立的Platform-Tools包,解压后将其路径(例如D:\android-sdk\platform-tools)添加到系统的PATH环境变量中。我不推荐使用某些第三方打包的“绿色版”或国内论坛下载的版本,它们可能版本老旧或带有未知修改,在执行一些需要特定权限的命令时可能出问题。
安装后,打开终端(Windows的CMD/PowerShell, macOS/Linux的Terminal),输入adb version来验证。接下来是最关键的一步:连接设备。
- 连接真机:在手机的“开发者选项”中开启“USB调试”。用数据线连接电脑后,在终端输入
adb devices。如果设备列表中出现你的设备并显示device状态,说明连接成功。如果显示unauthorized,你需要在手机弹出的“允许USB调试?”对话框中点击确认。 - 连接模拟器:对于Android Studio自带的模拟器或MuMu、夜神等第三方模拟器,ADB通常会自动连接。同样使用
adb devices查看,模拟器会以127.0.0.1:端口号的形式列出,例如127.0.0.1:7555。
注意:如果你同时连接了多个设备(比如一台真机和一个模拟器),在执行后续ADB命令时需要指定目标设备,使用
-s <设备序列号>参数,例如adb -s emulator-5554 shell ...。否则,ADB会随机选择一个设备,导致操作对象错误。
2.2 理解ADB Shell与系统属性
我们操作电池信息的核心战场是在adb shell环境中。adb shell命令让我们能进入连接设备的Linux命令行界面。电池信息在Android系统中,主要通过两种机制暴露:
- Linux内核的Power Supply子系统:信息存储在
/sys/class/power_supply/目录下的虚拟文件中。这是最底层的数据源。 - Android系统的BatteryService:它从内核读取信息,进行加工处理后,存储在系统属性(
system properties)和通过Binder服务提供给上层。我们通过dumpsys命令访问的就是这个服务。
因此,你需要熟悉几个关键的Shell命令:
cat:查看文件内容,用于读取/sys/class/power_supply/下的文件。dumpsys:转储(dump)系统服务的信息,功能极其强大。getprop/setprop:获取和设置系统属性。但请注意,绝大多数系统属性是只读的,尤其是像电池状态这种关键信息,普通ADB Shell(无root)无法直接修改。所谓的“设置”电池信息,通常需要通过其他间接手段模拟。
3. 深度获取:多种途径解析电池全量信息
获取信息不是简单地跑一个命令,理解每条数据背后的含义和来源更重要。下面我分层次介绍。
3.1 黄金标准:使用dumpsys battery命令
这是最常用、信息最全的方式。在adb shell中直接输入:
adb shell dumpsys battery你会看到类似下面的输出(不同设备、系统版本可能略有差异):
Current Battery Service state: AC powered: false USB powered: true Wireless powered: false Max charging current: 3000000 Max charging voltage: 9000000 Charge counter: 3500000 status: 2 health: 2 present: true level: 85 scale: 100 voltage: 4301 temperature: 320 technology: Li-ion我们来逐条解析关键字段,这比单纯看数字有用得多:
AC powered/USB powered/Wireless powered:当前充电类型。这是判断充电状态最直接的布尔值。status:电池状态码。这是一个关键数字,你需要知道它的含义:1:未知2:充电中3:放电中4:未充电5:满电
health:电池健康状态码。2:良好3:过热4:已损坏或电压异常5:过电压6:未知错误
level&scale:当前电量水平和最大刻度。电量百分比 =level/scale* 100。这里scale几乎总是100。voltage:当前电池电压,单位是毫伏(mV)。示例中的4301表示4.301V。temperature:电池温度,单位是0.1摄氏度。示例中的320表示32.0°C。这是一个非常重要的监控指标,温度过高是电池老化或存在问题的信号。technology:电池技术,如Li-ion(锂离子)、Li-poly(锂聚合物)。Charge counter:电荷计数器,单位是微安时(μAh),可以用于估算电池容量,但需要结合设计容量来分析。
实操心得:在自动化脚本中,我通常不会解析整个文本输出,而是使用grep和cut等命令提取特定字段。例如,只获取电量百分比:adb shell dumpsys battery | grep level | awk '{print $2}'。在Windows的CMD中,可能需要借助findstr。
3.2 底层窥探:查看/sys/class/power_supply/目录
对于想要深入研究或某些dumpsys信息不全的定制系统,可以直接查看内核数据。首先,进入该目录查看有哪些电源设备:
adb shell ls -l /sys/class/power_supply/你可能会看到battery,usb,wireless等目录。对于电池,我们关心battery/下的文件。
adb shell cat /sys/class/power_supply/battery/capacity # 当前电量百分比 adb shell cat /sys/class/power_supply/battery/status # 状态 (Charging, Discharging, Full等) adb shell cat /sys/class/power_supply/battery/health # 健康状态 adb shell cat /sys/class/power_supply/battery/temp # 温度 (单位可能是0.1摄氏度或直接是摄氏度,需验证) adb shell cat /sys/class/power_supply/battery/voltage_now # 当前电压 (微伏 uV) adb shell cat /sys/class/power_supply/battery/current_now # 当前电流 (微安 uA,正值表示放电,负值表示充电)重要区别:这里的数据更“原始”。例如,capacity可能和dumpsys的level一致,但voltage_now的单位是微伏(μV),而dumpsys中是毫伏(mV)。电流值在这里可以直接获取,而dumpsys通常不直接提供实时电流。
3.3 快捷查询:使用adb shell getprop命令
一些电池信息也会被缓存在系统属性中,但不如前两者全面和实时。
adb shell getprop | grep battery可能会看到像[ro.boot.fake_battery]: [0]或[sys.boot_completed]: [1]这类属性,直接相关的电池状态属性较少。dumpsys仍然是首选。
4. “设置”电池信息的真相与实战模拟方法
这是标题中最具迷惑性也最需要谨慎操作的部分。在非Root的普通调试环境下,你无法直接、永久地修改手机真实的物理电池状态。所谓的“设置”,实质上是对Android系统的BatteryService进行“欺骗”,让它以为电池处于某种状态,从而影响系统的行为(如触发低电量广播、进入省电模式)。这个功能主要服务于自动化测试。
4.1 官方测试命令:adb shell dumpsys battery
Android本身提供了用于测试的“设置”命令,但它修改的是BatteryService在内存中的状态,重启服务或设备后会重置。
设置电量百分比(模拟低电量):
adb shell dumpsys battery set level <数值>例如,模拟电量只剩15%:adb shell dumpsys battery set level 15。执行后,系统会立即广播ACTION_BATTERY_CHANGED,你的应用如果监听了这个广播,就会收到电量变化通知。状态栏的电量图标也会相应变化(在某些系统版本上可能不会立即刷新UI,但广播已发出)。
设置充电状态:
adb shell dumpsys battery set ac|usb|wireless|status <状态>- 设置AC充电:
adb shell dumpsys battery set ac 1(1开启,0关闭) - 设置USB充电:
adb shell dumpsys battery set usb 1 - 设置无线充电:
adb shell dumpsys battery set wireless 1 - 直接设置状态码:
adb shell dumpsys battery set status <2/3/4/5>(对应充电、放电、未充电、满电)
重置为真实状态:测试完成后,务必重置,让系统恢复读取真实硬件数据:
adb shell dumpsys battery reset4.2 进阶模拟:使用adb shell cmd命令
在Android 10(API 29)及以上版本,Google引入了更规范的cmd命令来与系统服务交互。操作电池服务的命令如下:
adb shell cmd battery set level <数值> adb shell cmd battery set status <状态码> adb shell cmd battery set ac|usb|wireless <0或1> adb shell cmd battery reset其效果与dumpsys battery set基本一致,但这是更推荐的新接口。
4.3 重要警告与局限性
- 仅影响软件层面:这些命令只欺骗了Android框架层。手机的真实硬件电量、充电电路完全不受影响。拔掉充电器,用
dumpsys battery set ac 1模拟的AC充电状态依然存在,但实际硬件并未充电。 - UI可能不同步:虽然广播发出了,但SystemUI(状态栏)更新可能滞后或不更新,这取决于手机厂商的系统定制。不要以状态栏图标作为测试成功的唯一标准,应以应用日志中收到的广播或
BatteryManagerAPI读取到的值为准。 - 需要后台测试权限:在Android Pie(API 28)以后,应用对电池状态信息的访问受到了限制。你的测试应用可能需要声明
REQUEST_IGNORE_BATTERY_OPTIMIZATIONS权限或在后台运行时能够正常接收广播,这需要在测试代码中处理好。 - 无法模拟所有属性:像
health(健康状态)、temperature(温度)等属性,通常无法通过上述简单命令设置。模拟这些需要更复杂的方法,例如使用可注入测试数据的模拟器(如Android Studio模拟器支持扩展控制面板设置电池温度),或者在拥有Root权限的设备上直接修改对应的系统文件或属性。
5. 自动化测试集成实战案例
理论说再多,不如看一个实际的应用场景。假设我们要为一个音乐播放器App编写一个自动化测试用例:“当电量低于20%且未在充电时,应自动暂停播放并弹出省电提示”。
我们使用Python脚本,结合adb命令和uiautomator2(一个流行的Android UI自动化框架)来模拟这个场景。
import subprocess import time import uiautomator2 as u2 def set_battery_via_adb(level, charging=False): """通过ADB设置电池状态""" # 重置确保状态干净 subprocess.run(['adb', 'shell', 'dumpsys', 'battery', 'reset'], capture_output=True) time.sleep(0.5) # 设置电量水平 subprocess.run(['adb', 'shell', 'dumpsys', 'battery', 'set', 'level', str(level)], capture_output=True) # 设置充电状态 if charging: subprocess.run(['adb', 'shell', 'dumpsys', 'battery', 'set', 'ac', '1'], capture_output=True) else: subprocess.run(['adb', 'shell', 'dumpsys', 'battery', 'set', 'ac', '0'], capture_output=True) subprocess.run(['adb', 'shell', 'dumpsys', 'battery', 'set', 'status', '3'], capture_output=True) # 放电状态 time.sleep(1) # 等待系统广播生效 print(f"模拟设置完成:电量{level}%,充电状态:{charging}") def test_low_battery_behavior(): # 连接设备 d = u2.connect() app_package = "com.example.musicplayer" # 1. 启动应用并开始播放音乐 d.app_start(app_package) time.sleep(2) d(resourceId="com.example.musicplayer:id/play_button").click() print("音乐开始播放...") time.sleep(3) # 2. 模拟电量降至15%,且未充电 set_battery_via_adb(15, charging=False) # 3. 等待并验证应用行为 time.sleep(3) # 给应用反应时间 # 检查是否弹出提示框(假设其文本包含“省电”) if d(textContains="省电").exists(timeout=5): print("✓ 低电量提示框成功弹出。") else: print("✗ 未检测到低电量提示框,可能逻辑有误或元素定位失败。") # 检查音乐是否暂停(假设暂停按钮状态变化) # 这里需要根据实际App的UI元素来判断 # 例如,播放按钮重新变为可点击状态 if d(resourceId="com.example.musicplayer:id/play_button").get_text() == "播放": print("✓ 音乐已自动暂停。") else: print("✗ 音乐可能未暂停。") # 4. 清理:重置电池状态,停止应用 subprocess.run(['adb', 'shell', 'dumpsys', 'battery', 'reset']) d.app_stop(app_package) print("测试结束,环境已清理。") if __name__ == "__main__": test_low_battery_behavior()脚本解析与避坑点:
- 重置的重要性:在每次设置前先
reset,避免之前测试的残留状态影响本次测试。 - 状态同步延迟:设置命令发出后,系统广播和App响应需要时间。
time.sleep(1)是简单的等待,在生产级测试框架中,应使用更智能的等待条件(如等待特定UI元素出现)。 - UI元素定位:
uiautomator2通过资源ID、文本等方式定位元素。你需要使用adb shell uiautomator dump或App的布局检查工具来获取正确的元素标识。这是UI自动化中最容易出错的部分。 - 广播接收验证:更严谨的测试应该在App内部打日志,或者通过
logcat过滤特定的广播接收日志来验证,这比依赖UI更可靠。
6. 高级议题与疑难排查
当你掌握了基础操作后,可能会遇到更复杂的需求或问题。
6.1 模拟电池温度与健康状态
如前所述,标准adb shell dumpsys battery set命令不支持设置温度和健康度。如果你确实需要测试高温保护或电池损坏的场景,有以下几种途径:
- 使用Android Studio模拟器:这是最方便的选择。在模拟器的扩展控制面板(Extended Controls)中,可以直接滑动设置电池温度和健康状态(Good, Failed, Dead等)。这完全在软件层面模拟,非常适合CI/CD集成测试。
- Root设备:在已Root的设备上,你可以尝试直接向底层接口写入数据。例如,某些设备上温度信息存储在
/sys/class/power_supply/battery/temp文件中,理论上可以echo一个值进去,但极其危险且强烈不推荐,可能破坏系统或需要内核支持,每台设备路径和格式都可能不同。 - Mock系统服务:在单元测试或插桩测试(Instrumentation Test)中,你可以使用AndroidX Test提供的
BatteryMocks或自己创建Mock对象来模拟BatteryManager的返回值和广播。这是面向代码层级的、更安全的测试方法。
6.2 排查“设置”无效的常见原因
如果你发现adb shell dumpsys battery set level 10之后,你的App没反应,可以按以下步骤排查:
- 确认命令生效:首先,立即执行
adb shell dumpsys battery,检查level和status字段是否已经变成了你设置的值。如果这里都没变,说明命令执行有问题(可能是设备未连接好,或Shell权限问题)。 - 检查广播:在终端运行
adb logcat | grep -i battery_changed,然后再次执行设置命令。观察日志中是否有ACTION_BATTERY_CHANGED广播被发出的记录。如果没有,可能是系统版本或定制ROM的问题。 - 验证App监听器:确保你的App正确注册了
ACTION_BATTERY_CHANGED广播接收器(动态注册或静态注册),并且没有因为进程被杀、省电策略等原因失效。可以在App代码里收到广播后打印一条Log,便于追踪。 - 检查后台限制:在Android 6.0+,App进入待机模式(Doze)后会被限制网络和后台作业;Android 8.0+对后台服务有严格限制;Android 10+限制了后台启动Activity。确保你的测试场景避开了这些限制,或者为测试应用申请了相应的豁免权限。
- 厂商定制:一些国内手机厂商的深度定制系统(如MIUI、EMUI)可能会修改电池相关的广播行为或增加自己的省电管理,导致标准Android行为不一致。这种情况下,需要在真机上针对特定型号进行适配和测试。
6.3 持续监控与数据记录
对于性能测试或功耗分析,你可能需要长时间记录电池信息的变化。可以写一个简单的Shell脚本循环抓取数据:
#!/bin/bash # 文件名:battery_logger.sh END_TIME=$((SECONDS + 60)) # 记录60秒 echo "Timestamp,Level(%),Status,Voltage(mV),Temperature(0.1°C)" > battery_log.csv while [ $SECONDS -lt $END_TIME ]; do TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S') BATTERY_INFO=$(adb shell dumpsys battery) LEVEL=$(echo "$BATTERY_INFO" | grep level | awk '{print $2}') STATUS=$(echo "$BATTERY_INFO" | grep status | awk '{print $2}') VOLTAGE=$(echo "$BATTERY_INFO" | grep voltage | awk '{print $2}') TEMP=$(echo "$BATTERY_INFO" | grep temperature | awk '{print $2}') echo "$TIMESTAMP,$LEVEL,$STATUS,$VOLTAGE,$TEMP" >> battery_log.csv sleep 2 # 每2秒采样一次 done echo "记录完成。"这个脚本会生成一个CSV文件,方便你导入到Excel或数据分析工具中绘制电量、电压、温度随时间变化的曲线,对于分析App的耗电情况非常有帮助。
掌握通过ADB获取和模拟电池信息,就像为你的Android开发和测试工作装上了一双“透视眼”和“遥控手”。它让你能更深入地理解设备状态,更高效地完成自动化测试。记住核心:获取信息是直接而强大的;而“设置”信息是一种用于特定测试场景的、临时性的模拟手段。在实际工作中,结合模拟器进行复杂场景模拟,在真机上主要进行验证,是最高效安全的策略。希望这篇详尽的梳理能帮你避开我当年摸索时踩过的那些坑。