在现现在的开发中, 电量消耗是一个应用运行效果的一个重要的衡量标准,尤为是直播,运动应用。 设备中的每一个硬件模块都会消耗电量。电量的最大消费者是CPU,但这只是系统的一个方面。一个编写良好的应用须要谨慎地使用电能。用户每每会删除耗电量大的应用。 除CPU外,耗电量高、值得关注的硬件模块还包括网络硬件、蓝牙、GPS、麦克风、加速计、摄像头、扬声器和屏幕。 如何下降电量的消耗,是延长使用时间的关键。咱们要关注如下:git
不论用户是否正在直接使用, CPU 都是应用所使用的主要硬件, 在后台操做和处理推送通知时, 应用仍然会消耗 CPU 资源。 github
应用计算的越多,消耗的电量越多.在完成相同的基本操做时, 老一代的设备会消耗更多的电量(换电池呀 哈哈哈 开个玩笑),计算量的消耗取决于不一样的因素。面试
没有单一原则能够减小设备中的执行次数,不少规则都取决于操做的本质, 如下是一些能够在应用中投入使用的最佳实践.算法
智能的网络访问管理可让应用响应的更快,并有助于延长电池寿命.在没法访问网络时,应该推迟后续的网络请求, 直到网络链接恢复为止。 此外,应避免在没有链接 WiFi 的状况下进行高宽带消耗的操做.好比视频流, 众所周知, 蜂窝无线系统(LTE,4G,3G等)对电量的消耗远远大于 WiFi信号, 根源在于 LTE 设备基于多输入,多输出技术,使用多个并发信号以维护两端的 LTE 连接,相似的,全部的蜂窝数据连接都会按期扫描以寻找更强的信号. 所以,咱们须要:服务器
官方提供了检查和监听网络状态的变化的代码,大多数人使用的网络库————AFNetWorking也提供了相似的代码,咱们能够任选其一,亦或是本身编写(这段代码并不复杂)markdown
定位服务包括GPS(或GLONASS)和WIFI硬件以及蜂窝网络网络
原文中只写了前两种,而咱们知道iOS的定位是有三种的数据结构
- 卫星定位
- 蜂窝基站定位
- Wi-Fi定位(WIFI定位的故事和原因颇有的一讲,在后面会说)
咱们都知道定位服务是很耗电的,使用 GPS 计算坐标须要肯定两点信息:并发
计算坐标会不断的使用 CPU 和 GPS 的硬件资源,所以他们会迅速的消耗电池电量app
先来看一下初始化CLLocationManager
并高效接受地理位置更新的典型代码
.h文件
@interface LLLocationViewController :UIViewController<CLLocationManagerDelegate>
@property (nonatomic, strong)CLLocationManager *manager;
@end
.m文件
@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 会利用这个机会向后台应用分享网络会话, 以便一些低优先级可以被处理, 如推送通知, 收取电子邮件等。 关键在于每当用户创建网络链接时,网络硬件都会在链接完成后多维持几秒的活动时间.每次集中的网络通讯都会消耗大量的电量 。 要想减轻这个问题带来的危害,你的软件须要有所保留的的使用网络.应该按期集中短暂的使用网络,而不是持续的保持着活动的数据流.只有这样,网络硬件才有机会关闭
这里iOS 10 以后变化比较大,参考便可
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
的通知事件来恢复动画
在视频播放期间,最好保持屏幕常量.可使用UIApplication
对象的 idleTimerDisabled
属性来实现这个目的.一旦设置了 YES, 他会阻止屏幕休眠,从而实现常亮. 与动画相似,你能够经过相应应用的通知来释放和获取锁
ps;iOS开发交流技术群:欢迎你的加入,无论你是大牛仍是小白都欢迎入驻 ,分享BAT,阿里面试题、面试经验,讨论技术, 你们一块儿交流学习成长
使用屏幕比休眠锁或暂停/恢复动画要复杂得多
若是正在播放电影或运行动画, 你能够将它们从设备的屏幕挪到外部屏幕,而只在设备的屏幕上保留最基本的设置,这样能够减小设备上的屏幕更新,进而延长电池寿命
处理这一场景的典型代码会涉及一下步骤
在启动期间监测屏幕的数量 若是屏幕数量大于1,则进行切换
监听屏幕在连接和断开时的通知. 若是有新的屏幕加入, 则进行切换. 若是全部的外部屏幕都被移除,则恢复到默认显示
@interface LLMultiScreenViewController () @property (nonatomic, strong)UIWindow *secondWindow; @end
@implementation LLMultiScreenViewController
}
}
}
(void)registerNotifications{
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; [nc addObserver:self selector:@selector(scrensChanged:) name:UIScreenDidConnectNotification object:nil];
}
}
(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];
}
当你的应用进入后台是, 应该释放对这些硬件的锁定:
基本规则: 只有当应用处于前台时才与这些硬件进行交互, 应用处于后台时应中止交互
不过扬声器和无线蓝牙可能例外, 若是你正在开发音乐,收音机或其余的音频类应用,则须要在应用进入后台后继续使用扬声器.不要让屏幕仅仅为音频播放的目的而保持常量.相似的, 若应用还有未完成的数据传输, 则须要在应用进入后台后持续使用无线蓝牙,例如,与其余设备传输文件
ps;iOS开发交流技术群:欢迎你的加入,无论你是大牛仍是小白都欢迎入驻 ,分享BAT,阿里面试题、面试经验,讨论技术, 你们一块儿交流学习成长
一个智能的应用会考虑到电池的电量和自身的状态, 从而决定是否执行资源密集消耗性的操做(好比扫二维码时的手电).另一个有价值的点是对充电的判断,肯定设备是否处于充电状态
来看一下此处的代码实施
- (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的一个滑块,代表了最低电量------在此示例中,用户能够自行调整),在实际项目中应用的开发人员一般根据操做的复杂性和密集性对阈值进行预设.不一样的密集型操做可能会有不一样的最低电量需求