一、私有方法获取电量相关的信息
- 可以获取的信息: 电池的健康程度、电池容量、是否充电、电源类型、传输类型等
#include <dlfcn.h>
- (NSDictionary*)fetchBatteryInfo
{
mach_port_t *kIOMasterPortDefault;
kern_return_t (*ioRegistryEntryCreateCFProperties)(mach_port_t entry, CFMutableDictionaryRef *properties, CFAllocatorRef allocator, UInt32 options) = NULL;
mach_port_t (*ioServiceGetMatchingService)(mach_port_t masterPort, CFDictionaryRef matching CF_RELEASES_ARGUMENT) = NULL;
CFMutableDictionaryRef (*ioServiceMatching)(const char *name) = NULL;
CFTypeRef (*ioPSCopyPowerSourcesInfo)(void) = NULL;
CFArrayRef (*ioPSCopyPowerSourcesList)(CFTypeRef blob) = NULL;
CFDictionaryRef (*ioPSGetPowerSourceDescription)(CFTypeRef blob, CFTypeRef ps) = NULL;
CFMutableDictionaryRef powerSourceService = NULL;
mach_port_t platformExpertDevice = 0;
var handle = dlopen("/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit", RTLD_LAZY);
ioRegistryEntryCreateCFProperties = dlsym(handle, "IORegistryEntryCreateCFProperties");
kIOMasterPortDefault = dlsym(handle, "kIOMasterPortDefault");
ioServiceMatching = dlsym(handle, "IOServiceMatching");
ioServiceGetMatchingService = dlsym(handle, "IOServiceGetMatchingService");
ioPSCopyPowerSourcesInfo = dlsym(handle, "IOPSCopyPowerSourcesInfo");
ioPSCopyPowerSourcesList = dlsym(handle, "IOPSCopyPowerSourcesList");
ioPSGetPowerSourceDescription = dlsym(handle, "IOPSGetPowerSourceDescription");
if (ioRegistryEntryCreateCFProperties != NULL &&
ioServiceMatching != NULL &&
ioServiceGetMatchingService != NULL)
{
powerSourceService = ioServiceMatching("IOPMPowerSource");
platformExpertDevice = ioServiceGetMatchingService(*kIOMasterPortDefault, powerSourceService);
}
NSMutableDictionary *batteryInfo = nil;
if(powerSourceService != NULL && platformExpertDevice != 0)
{
CFMutableDictionaryRef prop = NULL;
ioRegistryEntryCreateCFProperties(platformExpertDevice, &prop, 0, 0);
if(prop != NULL)
{
batteryInfo = [NSMutableDictionary dictionaryWithDictionary:(NSDictionary*)prop];
CFRelease(prop);
}
}
var blob = ioPSCopyPowerSourcesInfo();
var sources = ioPSCopyPowerSourcesList(blob);
CFDictionaryRef pSource = NULL;
var numOfSources = CFArrayGetCount(sources);
for(CFIndex i = 0; i < numOfSources; I++)
{
pSource = ioPSGetPowerSourceDescription(blob, CFArrayGetValueAtIndex(sources, i));
if(pSource != NULL)
{
if(batteryInfo == nil)
batteryInfo = [NSMutableDictionary dictionaryWithDictionary:(NSDictionary*)pSource];
else
[batteryInfo setValuesForKeysWithDictionary:(NSDictionary*)pSource];
break;
}
}
dlclose(handle);
CFRelease(blob);
CFRelease(sources);
return batteryInfo;
}
- 越高版本的数据越少,苹果审核很难通过
- iOS 12.2 iPhone X 结果如下
{
"Battery Provides Time Remaining" = 1;
BatteryHealth = Poor;
BatteryHealthCondition = "Check Battery";
BatteryInstalled = 1;
"Current Capacity" = 100;
ExternalConnected = 1;
"Is Charged" = 1;
"Is Charging" = 0;
"Is Present" = 1;
"Max Capacity" = 100;
Name = "InternalBattery-0";
"Power Source ID" = 2424931;
"Power Source State" = "AC Power";
"Raw External Connected" = 1;
"Show Charging UI" = 1;
"Time to Empty" = 0;
"Time to Full Charge" = 0;
"Transport Type" = Internal;
Type = InternalBattery;
}
- ios 10.3 iPhone 5c
{
AdapterInfo = 16384;
AtCriticalLevel = 0;
"Battery Provides Time Remaining" = 1;
BatteryHealth = Good;
Current = 1000;
"Current Capacity" = 87;
CurrentCapacity = 1131;
DesignCycleCount = 300;
ExternalChargeCapable = 1;
ExternalConnected = 1;
FullyCharged = 0;
IOClass = AppleARMPMUCharger;
"Is Charging" = 1;
"Is Finishing Charge" = 0;
"Is Present" = 1;
IsCharging = 1;
"Max Capacity" = 100;
MaxCapacity = 1300;
Name = "InternalBattery-0";
"Power Source ID" = 4063331;
"Power Source State" = "AC Power";
"Raw External Connected" = 1;
"Show Charging UI" = 1;
"Time to Empty" = 0;
"Time to Full Charge" = 0;
"Transport Type" = Internal;
Type = InternalBattery;
Voltage = 4178;
}
二、sysdiagnose
- 证书下载地址
- 安装方式
- Apple Configurator 2
- AirDrop
- 安装完成后在不重启手机的情况下,等待10到30分钟。
- 通过 iTunes同步到电脑上。
- 去 ~/Library/Logs/CrashReporter/MobileDevice 目录下,打开 powerlog_xxxx.PLSQL 文件
-
可以获取到最近几天的所有电量信息
三、系统提供接口
- 可以获取到整体的电池利用率,以及充电状态
// #import <UIKit / UIDevice.h>
UIDevice * device = [UIDevice currentDevice ];
device.batteryMonitoringEnabled = YES ;
//。的UIDevice返回的batteryLevel的范围在0到1之间
NSUInteger batteryLevel = device.batteryLevel * 100 ;
//获取充电状态
UIDeviceBatteryState state = device.batteryState;
if(state == UIDeviceBatteryStateCharging || state == UIDeviceBatteryStateFull){
//正在充电和电池已满
}
四、Ecergy Log
-
iOS设置选项 - >开发者选项 - >日志记录 - >开始录制
进行需要测试电量的场景操作后进入开发者选项点击停止录制
iOS设备和Mac连接
打开仪器,选择能量诊断
-
选择文件>从设备导入记录的数据
结合 Time Profiler 工具,可以大概定位能耗比较高的代码,参考地址
五、Energy Impact
可以大致确定程序运行中的能耗情况, 结合 Time Profiler 工具进行代码定位
-
真机运行项目,选择Energy Impact
蓝色表示--合理,黄色--表示程序比较耗电,红色--表示仅仅轻度使用你的程序,就会很耗电.
图表中Utilization栏中是表示瞬时耗电情况
图表中Average栏中,表示平均耗电情况
图表中Energy Impact中cost(蓝色)表示运行项目代码需要电量,Overhead(红色)表示开销,包括CPU的唤起,无线电模组(蓝牙&WiFi),和其他系统资源的调用等
灰色表示有电量消耗,白色表示没有电量消耗
-
影响电量的五个因素,
- CPU : 使用率超过20%就会快速耗干电池电量.高效使用CPU,并且当用户出现模糊输入时快速做出不做事情的反应.
- Network : 网络活动会唤起需要长时间周期性供电的无线电模组,可以分批次进行网络请求,来降低开销.
- Location : 精密&高频的的定位会增加开销,需要按需使用.
- GPU : 图形处理器(显卡的处理器),乱使用GPU会导致交互差,并且降低电池寿命.
- Background : 后台状态App仍会消耗电量,App要按需执行后台操作,并使用延迟APIs来保证系统运算高效执行.另外,在app进入后台状态是,立即减少动作,并且通知系统一次这些动作已经完成.