关于iOS电量检测和分析的调研

一、私有方法获取电量相关的信息

  • 可以获取的信息: 电池的健康程度、电池容量、是否充电、电源类型、传输类型等
#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

  • 证书下载地址
  • 安装方式
  • 安装完成后在不重启手机的情况下,等待10到30分钟。
  • 通过 iTunes同步到电脑上。
  • 去 ~/Library/Logs/CrashReporter/MobileDevice 目录下,打开 powerlog_xxxx.PLSQL 文件
  • 可以获取到最近几天的所有电量信息


    image.png

三、系统提供接口

  • 可以获取到整体的电池利用率,以及充电状态
 // #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设置选项 - >开发者选项 - >日志记录 - >开始录制


    image.png
  • 进行需要测试电量的场景操作后进入开发者选项点击停止录制

  • iOS设备和Mac连接

  • 打开仪器,选择能量诊断

  • 选择文件>从设备导入记录的数据


    image.png
  • 结合 Time Profiler 工具,可以大概定位能耗比较高的代码,参考地址

五、Energy Impact

  • 可以大致确定程序运行中的能耗情况, 结合 Time Profiler 工具进行代码定位

  • 真机运行项目,选择Energy Impact


    image.png
  • 蓝色表示--合理,黄色--表示程序比较耗电,红色--表示仅仅轻度使用你的程序,就会很耗电.

  • 图表中Utilization栏中是表示瞬时耗电情况

  • 图表中Average栏中,表示平均耗电情况

  • 图表中Energy Impact中cost(蓝色)表示运行项目代码需要电量,Overhead(红色)表示开销,包括CPU的唤起,无线电模组(蓝牙&WiFi),和其他系统资源的调用等

  • 灰色表示有电量消耗,白色表示没有电量消耗

  • 影响电量的五个因素,

    • CPU : 使用率超过20%就会快速耗干电池电量.高效使用CPU,并且当用户出现模糊输入时快速做出不做事情的反应.
    • Network : 网络活动会唤起需要长时间周期性供电的无线电模组,可以分批次进行网络请求,来降低开销.
    • Location : 精密&高频的的定位会增加开销,需要按需使用.
    • GPU : 图形处理器(显卡的处理器),乱使用GPU会导致交互差,并且降低电池寿命.
    • Background : 后台状态App仍会消耗电量,App要按需执行后台操作,并使用延迟APIs来保证系统运算高效执行.另外,在app进入后台状态是,立即减少动作,并且通知系统一次这些动作已经完成.
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容

  • 本次分享会目的 通过对增加对 Android Profile Tools 的了解,提高大家工作过程中定位和解决Bu...
    嘟嘟赌起阅读 7,191评论 0 4
  • 一、耗电量检测 1、进入手机"设置"->"电池",可以直观的看出来手机应用的耗电情况。 2、使用Xcode打开你的...
    彭磊PL阅读 2,151评论 0 2
  • 耗电量检测 1.进入手机"设置" -> "电池",可以直观的看出来手机应用的耗电情况. 2.使用xcode打开你的...
    陳俊峰阅读 34,296评论 13 116
  • 我要走了 你自保重 纵有千言万语 化作漫天繁星 雨丝深凝……
    弘毅123阅读 318评论 0 1
  • 题记:坚持一件事,难度系数是比较高的,上一篇文章《赵雷歌声中的“成都”,其实每个人心中都有》还没有写完,晚上时间已...
    微鸣艾特羊阅读 333评论 0 0