在编写高性能 代码时, 电量消耗是一个须要重点处理的重要因素, 就执行时间和 CPU 资源的利用而言, 咱们不只要实现高效的数据结构和算法, 还须要考虑其余的因素,若是某个应用是个电池黑洞,那么必定不会有人喜欢他 电量消耗除了 CPU 外,还有一些硬件模块:网络硬件, 蓝牙,GPS, 麦克风,加速计,摄像头,扬声器,和屏幕. 咱们能够带着如下问题来看这篇文章:算法
不论用户是否正在直接使用, CPU 都是应用所使用的主要硬件, 在后台操做和处理推送通知时, 应用仍然会消耗 CPU 资源 小程序
应用计算的越多,消耗的电量越多.在完成相同的基本操做时, 老一代的设备会消耗更多的电量(换电池呀 哈哈哈 开个玩笑),计算量的消耗取决于不一样的因素bash
没有单一原则能够减小设备中的执行次数,不少规则都取决于操做的本质, 如下是一些能够在应用中投入使用的最佳实践服务器
智能的网络访问管理可让应用响应的更快,并有助于延长电池寿命.在没法访问网络时,应该推迟后续的网络请求, 直到网络链接恢复为止. 此外,应避免在没有链接 WiFi 的状况下进行高宽带消耗的操做.好比视频流, 众所周知, 蜂窝无线系统(LTE,4G,3G等)对电量的消耗远远大于 WiFi信号, 根源在于 LTE 设备基于多输入,多输出技术,使用多个并发信号以维护两端的 LTE 连接,相似的,全部的蜂窝数据连接都会按期扫描以寻找更强的信号. 所以:咱们须要网络
这个知识点我项目中并无用到定位相关的功能 ,不过也总结一下书中所讲的知识点 有用的定位功能的朋友能够参考此知识点来优化本身的 app数据结构
咱们都知道定位服务是很耗电的,使用 GPS 计算坐标须要肯定两点信息:并发
计算坐标会不断的使用 CPU 和 GPS 的硬件资源,所以他们会迅速的消耗电池电量 先来看一下初始化CLLocationManager
并高效接受地理位置更新的典型代码app
#import "LLLocationViewController.h"
#import <CoreLocation/CoreLocation.h>
@interface LLLocationViewController ()<CLLocationManagerDelegate>
@property (nonatomic, strong)CLLocationManager *manager;
@end
@implementation LLLocationViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.manager = [[CLLocationManager alloc]init];
self.manager.delegate = self;
}
- (void)enableLocationButtonClick:(UIButton *)sender{
self.manager.distanceFilter = kCLDistanceFilterNone;
// 按照最大精度初始化管理器
self.manager.desiredAccuracy = kCLLocationAccuracyBest;
if (IS_IOS8) {
[self.manager requestWhenInUseAuthorization];
}
[self.manager startUpdatingLocation];
}
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray<CLLocation *> *)locations{
CLLocation *loc = [locations lastObject];
// 使用位置信息
}
复制代码
LocationManager:didUpdateLocations:
事件通知发生变化,该距离单位是 M距离过滤器只是软件层面的过滤器,而精度级别会影响物理天线的使用.当委托方法 LocationManager:didUpdateLocations:
被调用时,使用距离范围更普遍的过渡器只会影响间隔.另外一方面,更高的精度级别意味着更多的活动天线,这会消耗更多的能量数据结构和算法
判断什么时候须要跟踪位置的变化, 在须要跟踪的时候调用 startUpdatingLocation
方法, 无须跟踪时调用stopUpdatingLocation
方法.性能
当应用在后台运行或用户没有与别人聊天时,也应该关闭位置跟踪,也就说说,浏览媒体库,查看朋友列表或调整应用设置时, 都应该关闭位置跟踪
为了提升电量的使用效率, IOS 老是尽量地保持无线网络关闭.当应用须要创建网络链接时, IOS 会利用这个机会向后台应用分享网络会话, 以便一些低优先级可以被处理, 如推送通知, 收取电子邮件等 关键在于每当用户创建网络链接时,网络硬件都会在链接完成后多维持几秒的活动时间.每次集中的网络通讯都会消耗大量的电量 要想减轻这个问题带来的危害,你的软件须要有所保留的的使用网络.应该按期集中短暂的使用网络,而不是持续的保持着活动的数据流.只有这样,网络硬件才有机会关闭
CLLocationManager
提供了一个替代的方法来监听位置的更新. [self.manager startMonitoringSignificantLocationChanges]
能够帮助你在更远的距离跟踪运动.精确的值由内部决定,且与distanceFilter
无关 使用这一模式能够在应用进入后台后继续跟踪运动,典型的作法是在应用进入后台时执行startMonitoringSignificantLocationChanges
方法,而当应用回到前台时执行startUpdatingLocation
以下代码
- (void)applicationDidEnterBackground:(UIApplication *)application {
[self.manager stopUpdatingLocation];
[self.manager startMonitoringSignificantLocationChanges];
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
[self.manager stopMonitoringSignificantLocationChanges];
[self.manager startUpdatingLocation];
}
复制代码
在其余应用须要更多资源时, 后台的应用可能会被关闭.在这种状况下, 一旦发生位置变化,应用会被重启,于是须要从新初始化监听过程,若出现这种状况,application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
方法会受到键值为UIApplicationLaunchOptionsLocationKey
的条目 以下代码: 在应用关闭后从新初始化监听
- (void)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 因缺少资源而关闭应用后, 监测应用是否由于位置变化而被重启
if (launchOptions[UIApplicationLaunchOptionsLocationKey]) {
// 开启监测位置的变化
[self.manager startMonitoringSignificantLocationChanges];
}
}
复制代码
###四 屏幕 屏幕很是耗电, 屏幕越大就越耗电.固然,若是你的应用在前台运行且与用户进行交互,则势必会使用屏幕并消耗电量 这里仍然有一些方案能够优化屏幕的使用
当应用在前台时, 使用动画, 一旦应用进入了后台,则当即暂停动画.一般来讲,你能够经过监听 UIApplicationWillResignActiveNotification
或UIApplicationDIdEnterBackgroundNotification
的通知事件来暂停或中止动画,也能够经过监听UIApplicationDidBecomeActiveNotification
的通知事件来恢复动画
我在上家公司就是作视频类App的,当时就采用了这个技术 保持屏幕常亮
在视频播放期间,最好保持屏幕常量.可使用UIApplication
对象的 idleTimerDisabled
属性来实现这个目的.一旦设置了 YES, 他会阻止屏幕休眠,从而实现常亮. 与动画相似,你能够经过相应应用的通知来释放和获取锁
使用屏幕比休眠锁或暂停/恢复动画要复杂得多
若是正在播放电影或运行动画, 你能够将它们从设备的屏幕挪到外部屏幕,而只在设备的屏幕上保留最基本的设置,这样能够减小设备上的屏幕更新,进而延长电池寿命
处理这一场景的典型代码会涉及一下步骤
@interface LLMultiScreenViewController ()
@property (nonatomic, strong)UIWindow *secondWindow;
@end
@implementation LLMultiScreenViewController
- (void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
[self updateScreens];
}
- (void)viewDidDisappear:(BOOL)animated{
[super viewDidDisappear:animated];
[self disconnectFromScreen];
}
- (void)viewDidLoad {
[super viewDidLoad];
[self registerNotifications];
}
- (void)registerNotifications{
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self selector:@selector(scrensChanged:) name:UIScreenDidConnectNotification object:nil];
}
- (void)scrensChanged:(NSNotification *)nofi{
[self updateScreens];
}
- (void)updateScreens{
NSArray *screens = [UIScreen screens];
if (screens.count > 1) {
UIScreen *secondScreen = [screens objectAtIndex:1];
CGRect rect =secondScreen.bounds;
if (self.secondWindow == nil) {
self.secondWindow = [[UIWindow alloc]initWithFrame:rect];
self.secondWindow.screen = secondScreen;
LLScreen2ViewController *svc = [[LLScreen2ViewController alloc]init];
svc.parent = self;
self.secondWindow.rootViewController = svc;
}
self.secondWindow.hidden = NO;
}else{
[self disconnectFromScreen];
}
}
- (void)disconnectFromScreen{
if (self.secondWindow != nil) {
// 断开链接并释放内存
self.secondWindow.rootViewController = nil;
self.secondWindow.hidden = YES;
self.secondWindow = nil;
}
}
- (void)dealloc{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
复制代码
当你的应用进入后台是, 应该释放对这些硬件的锁定:
基本规则: 只有当应用处于前台时才与这些硬件进行交互, 应用处于后台时应中止交互
不过扬声器和无线蓝牙可能例外, 若是你正在开发音乐,收音机或其余的音频类应用,则须要在应用进入后台后继续使用扬声器.不要让屏幕仅仅为音频播放的目的而保持常量.相似的, 若应用还有未完成的数据传输, 则须要在应用进入后台后持续使用无线蓝牙,例如,与其余设备传输文件
这一条我发现 摩拜单车小程序 作的挺好的,若是晚上骑车扫描二维码的话是须要开闪光灯达到照亮二维码的效果, 可是若是你的手机处于低电量的话 ,你的闪光灯是打不开的, 这一个细节就说明了用户体验很重要,他首先会保证不让你的手机由于闪光灯而直接关机
一个智能的应用会考虑到电池的电量和自身的状态, 从而决定是否执行资源密集消耗性的操做.另一个有价值的点是对充电的判断,肯定设备是否处于充电状态
来看一下此处的代码实施
- (BOOL)shouldProceedWithMinLevel:(NSUInteger)minLevel{
UIDevice *device = [UIDevice currentDevice];
// 打开电池监控
device.batteryMonitoringEnabled = YES;
UIDeviceBatteryState state = device.batteryState;
// 在充电或电池已经充满的状况下,任何操做均可以执行
if (state == UIDeviceBatteryStateCharging ||
state == UIDeviceBatteryStateFull) {
return YES;
}
// UIdevice 返回的 batteryLevel 的范围在0.00 ~ 1.00
NSUInteger batteryLevel = (NSUInteger)(device.batteryLevel * 100);
if (batteryLevel >= minLevel) {
return YES;
}
return NO;
}
复制代码
咱们也能够获得应用对 CPU 的利用率
// 须要导入这两个头文件
#import <mach/mach.h>
#import <assert.h>
- (float)appCPUUsage{
kern_return_t kr;
task_info_data_t info;
mach_msg_type_number_t infoCount = TASK_INFO_MAX;
kr = task_info(mach_task_self(), TASK_BASIC_INFO, info, &infoCount);
if (kr != KERN_SUCCESS) {
return -1;
}
thread_array_t thread_list;
mach_msg_type_number_t thread_count;
thread_info_data_t thinfo;
mach_msg_type_number_t thread_info_count;
thread_basic_info_t basic_info_th;
kr = task_threads(mach_task_self(), &thread_list, &thread_count);
if (kr != KERN_SUCCESS) {
return -1;
}
float tot_cpu = 0;
int j;
for (j = 0; j < thread_count; j++) {
thread_info_count = THREAD_INFO_MAX;
kr = thread_info(thread_list[j], THREAD_BASIC_INFO, thinfo, &thread_info_count);
if (kr != KERN_SUCCESS) {
return -1;
}
basic_info_th = (thread_basic_info_t)thinfo;
if (!(basic_info_th -> flags & TH_FLAGS_IDLE)) {
tot_cpu += basic_info_th -> cpu_usage / TH_USAGE_SCALE * 100.0;
}
}
vm_deallocate(mach_task_self(), (vm_offset_t)thread_list, thread_count * sizeof(thread_t));
return tot_cpu;
}
复制代码
当剩余电量较低时,提醒用户,并请求用户受权执行电源密集型的操做,---固然,只在 用户赞成的前提下执行 老是用一个指示符(也就是进度条百分比)显示长时间任务的进度, 包括设备上即将完成的计算或者只是下载一些内容.向用户提供完成进度的估算, 以帮助他们决定是否须要为设备充电
如下的最佳实践能够确保对电量的谨慎使用, 遵循如下要点,应用能够实现对电量的高效使用.
下边代码展现了设置电量的阈值以提示用户.
- (IBAction)onIntensiveOperationButtonClick:(id)sender {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
BOOL prompt = [defaults boolForKey:@"promptForBattery"];
int minLevel = [defaults integerForKey:@"minBatteryLevel"];
BOOL canAutoProceed = [self shouldProceeWithMinLevel:minLevel];
if (canAutoProceed) {
[self executeIntensiveOperation];
}else{
if (prompt) {
UIAlertView *view = [[UIAlertView alloc]initWithTitle:@"提示" message:@"电量低于最小值,是否继续执行" delegate: self cancelButtonTitle:@"取消" otherButtonTitles:@"肯定"];
[view show];
}else{
[self queueIntensiveOperation];
}
}
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
if (buttonIndex == 0) {
[self queueIntensiveOperation];
}else{
[self executeIntensiveOperation];
}
}
复制代码
代码对应的配图以下
promptForBattery
(应用设置中的拨动开关,代表是否要在低电量时给予提示)和miniBatteryLevel
(区间为0~100的一个滑块,代表了最低电量------在此示例中,用户能够自行调整),在实际项目中应用的开发人员一般根据操做的复杂性和密集性对阈值进行预设.不一样的密集型操做可能会有不一样的最低电量需求用户老是随身携带者手机,因此编写省电的代码就格外重要, 毕竟手机的移动电源并非随处可见,不过如今北京的街电共享充电宝好像很不错 本人逛街会常用街电充电宝,但仍是要尽量的为用户省电 在没法下降任务复杂性时, 提供一个对电池电量保持敏感的方案并在适当的时机提示用户, 会让用户感受很良好, 而且所以会成为你 APP 的永久用户