《高性能iOS 应用开发》之降低你 APP 的电量消耗
在編寫高性能 代碼時, 電量消耗是一個需要重點處理的重要因素, 就執(zhí)行時間和 CPU 資源的利用而言, 我們不僅要實現(xiàn)高效的數(shù)據(jù)結構和算法, 還需要考慮其他的因素,如果某個應用是個電池黑洞,那么一定不會有人喜歡他電量消耗除了 CPU 外,還有一些硬件模塊:網(wǎng)絡硬件, 藍牙,GPS, 麥克風,加速計,攝像頭,揚聲器,和屏幕.我們可以帶著以下問題來看這篇文章:
- 消耗電量的關鍵領域有哪些
- 如何降低電量的消耗
- 如何在 IOS 應用中分析電源, CPU 和資源的使用
一 CPU
不論用戶是否正在直接使用, CPU 都是應用所使用的主要硬件, 在后臺操作和處理推送通知時, 應用仍然會消耗 CPU 資源
應用計算的越多,消耗的電量越多.在完成相同的基本操作時, 老一代的設備會消耗更多的電量(換電池呀 哈哈哈 開個玩笑),計算量的消耗取決于不同的因素
- 對數(shù)據(jù)的處理
- 待處理的數(shù)據(jù)大小---更大的顯示屏允許軟件在單個視圖中展示更多的信息,但這也意味著要處理更多的數(shù)據(jù)
- 處理數(shù)據(jù)的算法和數(shù)據(jù)結構
- 執(zhí)行更新的次數(shù),尤其是在數(shù)據(jù)更新后,觸發(fā)應用的狀態(tài)或 UI 進行更新(應用收到的推送通知也會導致數(shù)據(jù)更新,如果此用戶正在使用應用,你還需要更新 UI)
沒有單一原則可以減少設備中的執(zhí)行次數(shù),很多規(guī)則都取決于操作的本質(zhì), 以下是一些可以在應用中投入使用的最佳實踐
- 針對不同的情況選擇優(yōu)化的算法例如,當你在排序時,如果列表少于43個實例, 則插入排序優(yōu)于歸并排序, 但實例對于286時, 應當使用快速排序,要優(yōu)先使用雙樞軸快速排序而不是傳統(tǒng)的單樞軸快速排序
- 如果應用從服務器接受數(shù)據(jù),盡量減少需要在客戶端進行的處理例如如果一段文字需要在客戶端進行渲染,盡可能在服務器將數(shù)據(jù)清理干凈我曾經(jīng)做個一個項目, 因為服務器的實現(xiàn)主要用于服務桌面用戶,所以返回的文本中包含 HTML 標簽, 清理 HTML 標簽的工作并沒有放在客戶端進行, 而是放在了服務端實現(xiàn),從而減少了設備上的計算過程, 降低了處理時間
- 優(yōu)化靜態(tài)編譯(ahead-of-time,AOT)處理動態(tài)編譯處理的缺點在于他會強制用戶等待操作完成, 但是激進的 AOT 處理則會導致計算資源的浪費, 需要根據(jù)應用和設備選擇精確定量的 AOT 處理.例如,在 UITableView 中渲染一組記錄時,在載入列表是處理全部的記錄并不是明智之舉,基于單元格的高度,如果設備可以渲染 N 條記錄, 那么3N 或4N 則是一個理想的數(shù)據(jù)載入規(guī)模, 類似的,用戶快速滑動,則不應立即載入記錄,而應推遲帶滾動速度下降到某一閾值.精確的閾值應該由每個單元格的處理時間和單元格的 UI 的復雜性來決定
二 網(wǎng)絡
智能的網(wǎng)絡訪問管理可以讓應用響應的更快,并有助于延長電池壽命.在無法訪問網(wǎng)絡時,應該推遲后續(xù)的網(wǎng)絡請求, 直到網(wǎng)絡連接恢復為止.此外,應避免在沒有連接 WiFi 的情況下進行高寬帶消耗的操作.比如視頻流, 眾所周知, 蜂窩無線系統(tǒng)(LTE,4G,3G等)對電量的消耗遠遠大于 WiFi信號, 根源在于 LTE 設備基于多輸入,多輸出技術,使用多個并發(fā)信號以維護兩端的 LTE 鏈接,類似的,所有的蜂窩數(shù)據(jù)鏈接都會定期掃描以尋找更強的信號.因此:我們需要
- 在進行任何網(wǎng)絡操作之前,先檢查合適的網(wǎng)絡連接是否可用
- 持續(xù)監(jiān)視網(wǎng)絡的可用性,并在鏈接狀態(tài)發(fā)生變化時給與適當?shù)姆答?
三 定位管理器和 GPS
這個知識點我項目中并沒有用到定位相關的功能 ,不過也總結一下書中所講的知識點 有用的定位功能的朋友可以參考此知識點來優(yōu)化自己的 app
我們都知道定位服務是很耗電的,使用 GPS 計算坐標需要確定兩點信息:
- 時間鎖每個 GPS 衛(wèi)星每毫秒廣播唯一一個1023位隨機數(shù), 因而數(shù)據(jù)傳播速率是1.024Mbit/s GPS 的接收芯片必須正確的與衛(wèi)星的時間鎖槽對齊
- 頻率鎖GPS 接收器必須計算由接收器與衛(wèi)星的相對運動導致的多普勒偏移帶來的信號誤差
計算坐標會不斷的使用 CPU 和 GPS 的硬件資源,因此他們會迅速的消耗電池電量先來看一下初始化CLLocationManager并高效接受地理位置更新的典型代碼
#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 *)managerdidUpdateLocations:(NSArray<CLLocation *> *)locations{CLLocation *loc = [locations lastObject];// 使用位置信息 }3.1 最佳的初始化
- distanceFilter
只要設備的移動超過了最小的距離, 距離過濾器就會導致管理器對委托對象的 LocationManager:didUpdateLocations:事件通知發(fā)生變化,該距離單位是 M - desiredAccuracy
精度參數(shù)的使用直接影響了使用天線的個數(shù), 進而影響了對電池的消耗.精度級別的選取取決于應用的具體用途,精度是一個枚舉 我們應該依照不同的需求去恰當?shù)倪x取精度級別
距離過濾器只是軟件層面的過濾器,而精度級別會影響物理天線的使用.當委托方法 LocationManager:didUpdateLocations:被調(diào)用時,使用距離范圍更廣泛的過渡器只會影響間隔.另一方面,更高的精度級別意味著更多的活動天線,這會消耗更多的能量
3.2 關閉無關緊要的特性
判斷何時需要跟蹤位置的變化, 在需要跟蹤的時候調(diào)用 startUpdatingLocation方法, 無須跟蹤時調(diào)用stopUpdatingLocation方法.
當應用在后臺運行或用戶沒有與別人聊天時,也應該關閉位置跟蹤,也就說說,瀏覽媒體庫,查看朋友列表或調(diào)整應用設置時, 都應該關閉位置跟蹤
3.3 只在必要時使用網(wǎng)絡
為了提高電量的使用效率, IOS 總是盡可能地保持無線網(wǎng)絡關閉.當應用需要建立網(wǎng)絡連接時, IOS 會利用這個機會向后臺應用分享網(wǎng)絡會話, 以便一些低優(yōu)先級能夠被處理, 如推送通知, 收取電子郵件等
關鍵在于每當用戶建立網(wǎng)絡連接時,網(wǎng)絡硬件都會在連接完成后多維持幾秒的活動時間.每次集中的網(wǎng)絡通信都會消耗大量的電量
要想減輕這個問題帶來的危害,你的軟件需要有所保留的的使用網(wǎng)絡.應該定期集中短暫的使用網(wǎng)絡,而不是持續(xù)的保持著活動的數(shù)據(jù)流.只有這樣,網(wǎng)絡硬件才有機會關閉
3.4 后臺定位服務
CLLocationManager提供了一個替代的方法來監(jiān)聽位置的更新. [self.manager startMonitoringSignificantLocationChanges]可以幫助你在更遠的距離跟蹤運動.精確的值由內(nèi)部決定,且與distanceFilter無關
使用這一模式可以在應用進入后臺后繼續(xù)跟蹤運動,典型的做法是在應用進入后臺時執(zhí)行startMonitoringSignificantLocationChanges方法,而當應用回到前臺時執(zhí)行startUpdatingLocation 如下代碼
3.5 在應用關閉后重啟
在其他應用需要更多資源時, 后臺的應用可能會被關閉.在這種情況下, 一旦發(fā)生位置變化,應用會被重啟,因而需要重新初始化監(jiān)聽過程,若出現(xiàn)這種情況,application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions方法會受到鍵值為UIApplicationLaunchOptionsLocationKey的條目
如下代碼: 在應用關閉后重新初始化監(jiān)聽
四 屏幕
屏幕非常耗電, 屏幕越大就越耗電.當然,如果你的應用在前臺運行且與用戶進行交互,則勢必會使用屏幕并消耗電量
這里仍然有一些方案可以優(yōu)化屏幕的使用
4.1 動畫
當應用在前臺時, 使用動畫, 一旦應用進入了后臺,則立即暫停動畫.通常來說,你可以通過監(jiān)聽 UIApplicationWillResignActiveNotification或UIApplicationDIdEnterBackgroundNotification的通知事件來暫?;蛲V箘赢?也可以通過監(jiān)聽UIApplicationDidBecomeActiveNotification的通知事件來恢復動畫
4.2 視頻播放
我在上家公司就是做視頻類App的,當時就采用了這個技術 保持屏幕常亮
在視頻播放期間,最好保持屏幕常量.可以使用UIApplication對象的 idleTimerDisabled屬性來實現(xiàn)這個目的.一旦設置了 YES, 他會阻止屏幕休眠,從而實現(xiàn)常亮.
與動畫類似,你可以通過相應應用的通知來釋放和獲取鎖
4.3 多屏幕
使用屏幕比休眠鎖或暫停/恢復動畫要復雜得多
如果正在播放電影或運行動畫, 你可以將它們從設備的屏幕挪到外部屏幕,而只在設備的屏幕上保留最基本的設置,這樣可以減少設備上的屏幕更新,進而延長電池壽命
處理這一場景的典型代碼會涉及一下步驟
- 1 在啟動期間監(jiān)測屏幕的數(shù)量 如果屏幕數(shù)量大于1,則進行切換
- 2 監(jiān)聽屏幕在鏈接和斷開時的通知. 如果有新的屏幕加入, 則進行切換. 如果所有的外部屏幕都被移除,則恢復到默認顯示
五 其他硬件
當你的應用進入后臺是, 應該釋放對這些硬件的鎖定:
- 藍牙
- 相機
- 揚聲器,除非應用是音樂類的
- 麥克風
基本規(guī)則: 只有當應用處于前臺時才與這些硬件進行交互, 應用處于后臺時應停止交互
不過揚聲器和無線藍牙可能例外, 如果你正在開發(fā)音樂,收音機或其他的音頻類應用,則需要在應用進入后臺后繼續(xù)使用揚聲器.不要讓屏幕僅僅為音頻播放的目的而保持常量.類似的, 若應用還有未完成的數(shù)據(jù)傳輸, 則需要在應用進入后臺后持續(xù)使用無線藍牙,例如,與其他設備傳輸文件
六 電池電量與代碼感知
這一條我發(fā)現(xiàn) 摩拜單車小程序 做的挺好的,如果晚上騎車掃描二維碼的話是需要開閃光燈達到照亮二維碼的效果, 但是如果你的手機處于低電量的話 ,你的閃光燈是打不開的, 這一個細節(jié)就說明了用戶體驗很重要,他首先會保證不讓你的手機因為閃光燈而直接關機
一個智能的應用會考慮到電池的電量和自身的狀態(tài), 從而決定是否執(zhí)行資源密集消耗性的操作.另外一個有價值的點是對充電的判斷,確定設備是否處于充電狀態(tài)
來看一下此處的代碼實施
- (BOOL)shouldProceedWithMinLevel:(NSUInteger)minLevel{UIDevice *device = [UIDevice currentDevice];// 打開電池監(jiān)控device.batteryMonitoringEnabled = YES;UIDeviceBatteryState state = device.batteryState;// 在充電或電池已經(jīng)充滿的情況下,任何操作都可以執(zhí)行if (state == UIDeviceBatteryStateCharging ||state == UIDeviceBatteryStateFull) {return YES;} // UIdevice 返回的 batteryLevel 的范圍在0.00 ~ 1.00NSUInteger 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;}當剩余電量較低時,提醒用戶,并請求用戶授權執(zhí)行電源密集型的操作,---當然,只在 用戶同意的前提下執(zhí)行
總是用一個指示符(也就是進度條百分比)顯示長時間任務的進度, 包括設備上即將完成的計算或者只是下載一些內(nèi)容.向用戶提供完成進度的估算, 以幫助他們決定是否需要為設備充電
七 最佳實踐
以下的最佳實踐可以確保對電量的謹慎使用, 遵循以下要點,應用可以實現(xiàn)對電量的高效使用.
- 最小化硬件使用. 換句話說,盡可能晚的與硬件打交道, 并且一旦完成任務立即結束使用
- 在進行密集型任務前, 檢查電池電量和充電狀態(tài)
- 在電量低時, 提示用戶是否確定要執(zhí)行任務,并在用戶同意后再執(zhí)行
- 或提供設置的選項,允許用戶定義電量的閾值,以便在執(zhí)行秘籍型操作前提示用戶
下邊代碼展示了設置電量的閾值以提示用戶.
- (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:@"電量低于最小值,是否繼續(xù)執(zhí)行" 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(區(qū)間為0~100的一個滑塊,表明了最低電量------在此示例中,用戶可以自行調(diào)整),在實際項目中應用的開發(fā)人員通常根據(jù)操作的復雜性和密集性對閾值進行預設.不同的密集型操作可能會有不同的最低電量需求
- 在實際執(zhí)行密集操作之前,檢查當前電量是否足夠, 或者手機是否正在充電.這就是我們判斷是否可以進行后續(xù)處理的邏輯,圖中你可以有自己的定制---最低電量和充電狀態(tài)
用戶總是隨身攜帶者手機,所以編寫省電的代碼就格外重要, 畢竟手機的移動電源并不是隨處可見,不過現(xiàn)在北京的街電共享充電寶好像很不錯 本人逛街會經(jīng)常使用街電充電寶,但還是要盡可能的為用戶省電
在無法降低任務復雜性時, 提供一個對電池電量保持敏感的方案并在適當?shù)臅r機提示用戶, 會讓用戶感覺很良好, 并且因此會成為你 APP 的永久用戶
作者:奧卡姆剃須刀
鏈接:http://www.jianshu.com/p/e2ad6f204271
來源:簡書
著作權歸作者所有。商業(yè)轉載請聯(lián)系作者獲得授權,非商業(yè)轉載請注明出處。
總結
以上是生活随笔為你收集整理的《高性能iOS 应用开发》之降低你 APP 的电量消耗的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Keil5软件仿真debug闪退问题
- 下一篇: 聚观早报 | 苹果被曝开发16英寸iPa