在現現在的開發中, 電量消耗是一個應用運行效果的一個重要的衡量標準,尤爲是直播,運動應用。 設備中的每一個硬件模塊都會消耗電量。電量的最大消費者是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的一個滑塊,代表了最低電量------在此示例中,用戶能夠自行調整),在實際項目中應用的開發人員一般根據操做的複雜性和密集性對閾值進行預設.不一樣的密集型操做可能會有不一樣的最低電量需求