在編寫高性能 代碼時, 電量消耗是一個須要重點處理的重要因素, 就執行時間和 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 的永久用戶