1.加载动态链接库
iOS是给予Linux内核,在Linux调用如下函数来加载动态链接库:dlopen,dlsym,dlclose,dlerror
void * dlopen(const char *filename, int flag);
char * dlerror(void);
void * dlsym(void *handle, const *symbol);
int dlclose(void *handle)
dlopen:以指定模式flag打开指定路径filename下的动态链接库文件,并返回一个句柄;其中flag有如下值:
#define RTLD_LAZY 0x1 //对于动态库未定义的变量不执行解析
#define RTLD_NOW 0x2 //解析每个未定义变量的地址,若解析不出来,抛NULL异常
#define RTLD_LOCAL 0x4 //使得库中的解析的定义变量只在当前可以使用
#define RTLD_GLOBAL 0x8 //使得库中的解析的定义变量在随后的其它的链接库中变得可以使用
dlerror:返回错误信息
dlsym:通过句柄和连接符名称获取函数名或者变量名,一般用于调用私有的API或者私有变量
dlclose:卸载打开的动态链接库
2.获取电量
为了获取电量mAh的形式,需要使用IOKit.framework。但是此类库为私有类库,所以使用的时候需要动态引用。伪代码如下:
a)动态链接IOKit.framework,获取并定义函数指针和变量地址
// 初始化电量管理函数
- (BOOL) batteryMgInit{
// 1. 动态获取IOKit句柄,选择RTLD_NOW模式
_handleIOKit =dlopen(NT_IOKIT_DYLIB_PATH, RTLD_NOW);
if (!_handleIOKit) {
return NO;
}
// 2. 通过IOKit句柄,动态获取kIOMasterPortDefault对应的mach port
_kIOMasterPortDefault = (mach_port_t *)dlsym(_handleIOKit, "kIOMasterPortDefault");
// 3. 通过IOKit句柄,动态获取主IOServiceMatching变量
_mIOServiceMathcing = (NT_IO_SERVICE_MATCHING)dlsym(_handleIOKit, "IOServiceMatching");
// 4. 通过IOKit句柄,动态获取IOServiceGetMatchingService对应的mach port
_mIOServiceGetMatchingService = (NT_IO_SERVICE_GET_MATCHING_SERVICE)dlsym(_handleIOKit, "IOServiceGetMatchingService");
// 5. 通过IOKit句柄,动态获取主IORegistryEntryCreateCFProperties函数地址
_mIORegistryEntryCreateCFProperties = (NT_IO_REGISTRY_ENTRY_CREATE_CF_PROPERTIES)dlsym(_handleIOKit, "IORegistryEntryCreateCFProperties");
// 6. GT_PFN_IOOBJECTRELEASE为int类型
_mIOObjectRelease = (NT_IO_OBJECT_RELEASE)dlsym(_handleIOKit, "IOObjectRelease");
if (_kIOMasterPortDefault &&
_mIOServiceMathcing &&
_mIOServiceGetMatchingService &&
_mIORegistryEntryCreateCFProperties &&
_mIOObjectRelease
) {
return YES;
}
return YES;
}
b)获取电量参数
- (void) updateBatteryInfo {
CFMutableDictionaryRef matching, properties = NULL;
mach_port_t entry = 0;
// 获取电量管理对象,_mIOServiceMathcing为动态获取IOKit对应函数(_mIOServiceMathcing)地址指针
// 调用IOKit的IOServiceMathcing函数,获取IOPMPowerSource对应的matching字典
matching = _mIOServiceMathcing("IOPMPowerSource");
if (!matching) {
return;
}
//
// func IOServiceGetMatchingService(_ masterPort: mach_port_t,
// _ matching: CFDictionary!) -> io_service_t
// 查询匹配matching的IOService注册对象,
entry = _mIOServiceGetMatchingService(*_kIOMasterPortDefault, matching);
if (!entry) {
return;
}
/**
函数原型:
func IORegistryEntryCreateCFProperties(_ entry: io_registry_entry_t,
_ properties: UnsafeMutablePointer?>!,
_ allocator: CFAllocator!,
_ options: IOOptionBits) -> kern_return_t
IORegistryEntryCreateCFProperties:根据注册对象句柄,将对象属性存入字典 ***/
kern_return_t rt = _mIORegistryEntryCreateCFProperties(entry, &properties, NULL, 0);
if (rt) {
return;
}
// properties提取参数
[self updateBatteryItem: (__bridge NSDictionary *)properties];
CFRelease( properties );
_mIOObjectRelease( entry );
return;
}
- (void) updateBatteryItem:(NSDictionary *)dic{
self.batteryUnit.preCurrentCapacity = self.batteryUnit.currentCapacity;
NSNumber *currentCapacity = [dic objectForKey: @"CurrentCapacity"];
self.batteryUnit.currentCapacity = [currentCapacity intValue];
NSNumber *maxCapacity = [dic objectForKey: @"MaxCapacity"];
self.batteryUnit.maxCapacity = [maxCapacity intValue];
NSNumber *voltage = [dic objectForKey:@"Voltage"];
self.batteryUnit.voltage = [voltage intValue];
}
实验结果:
demo程序获取电池当前电量,最大电量,电压。
如图分别采样90s,和112s两个时间节点的电量信息
image.png
90s采样截图
5.png
120s采样截图
6.png
遇到的问题:由于电池的物理特性,获取的当前电池电量,最大电池电量的值会发生变化。如最大电池某个采样点为1500,下个采样点为1000;其次上一时刻的当前电量可能会大于这一时刻的电量。
基于上诉原因,需要当前电量和最大电池电量配合使用才能较正确的表现当前电池电量情况,即剩余电量百分比(当前电池电量/最大电池电量)