本文是來(lái)自@ShawnSun的投稿
一款好的App一定要有非常好的用戶體驗(yàn),這一點(diǎn)已經(jīng)是大多數(shù)開(kāi)發(fā)者的共識(shí)。功耗是用戶體驗(yàn)中一個(gè)重要的組成部分,但這部分因?yàn)楦鞣N問(wèn)題,很多時(shí)候會(huì)被大家忽略。之前公司讓我在內(nèi)部搞個(gè)功耗優(yōu)化的培訓(xùn),但我發(fā)現(xiàn)網(wǎng)上相關(guān)的文章非常少,而且大多不系統(tǒng),也不夠權(quán)威。索性找到蘋(píng)果官方文檔,邊翻譯邊整理,就有了這邊文章。內(nèi)容有點(diǎn)長(zhǎng),大家可以收藏一下,以后慢慢看
當(dāng)app更新UI、執(zhí)行網(wǎng)絡(luò)操作或者在CPU中運(yùn)行代碼時(shí),都會(huì)消耗電能。隨著用戶越來(lái)越依賴電池、app數(shù)量的激增,電池能效成了用戶體驗(yàn)的重要組成部分。
良好的用戶體驗(yàn)需要如下要素:
電池壽命長(zhǎng)。隨著能效降低,電池壽命也會(huì)降低。但用戶想讓自己的移動(dòng)設(shè)備全天候待命。
速度快。iOS系統(tǒng)處理復(fù)雜操作時(shí)仍能提供很好的性能。
響應(yīng)快。同一時(shí)刻消耗太多資源會(huì)使UI卡頓,響應(yīng)用戶速度變慢。
溫度低。app消耗的硬件資源的越多,系統(tǒng)工作越繁重,設(shè)備的溫度就會(huì)逐漸上升。這時(shí)系統(tǒng)會(huì)通過(guò)一些措施降低設(shè)備溫度。
iOS運(yùn)用了很多先進(jìn)的節(jié)能技術(shù)確保用戶有很好的用戶體驗(yàn),包括軟硬件配合優(yōu)化、先進(jìn)的App調(diào)度機(jī)制、網(wǎng)絡(luò)延時(shí)操作、任務(wù)優(yōu)先級(jí)管理機(jī)制等。
App中很小的低效行為在整個(gè)系統(tǒng)中累加后,會(huì)對(duì)電池壽命、性能、響應(yīng)速度和溫度產(chǎn)生明顯的影響。作為app開(kāi)發(fā)人員,我們有責(zé)任確保我們的app盡可能高效地運(yùn)行。使用蘋(píng)果推薦的API,以確保系統(tǒng)可以正確地判斷如何更好地管理我們的app和app使用的各種資源。分批、減少網(wǎng)絡(luò)操作。盡可能避免不需要的UI刷新。功耗大的操作應(yīng)該在用戶的控制之中。比如,如果用戶正在玩一個(gè)視圖非常復(fù)雜的大型游戲,電量消耗很快用戶是可以理解的。不響應(yīng)用戶操作時(shí),app盡量不要執(zhí)行任何操作。
基本概念
沒(méi)有一勞永逸地解決能耗問(wèn)題的方案。很多技術(shù)和操作影響著電量的使用:
CPU。CPU是電能消耗大戶,高CPU使用量會(huì)迅速消耗掉用戶的電池電量。app做的每件事幾乎都需要用CPU,所以使用CPU要精打細(xì)算,真正有需要時(shí)通過(guò)分批、定時(shí)、有序地執(zhí)行任務(wù)。
設(shè)備喚醒。iOS設(shè)備通過(guò)睡眠來(lái)節(jié)能。只要設(shè)備被喚醒,屏幕和其他的硬件資源就必須通電,會(huì)產(chǎn)生很高的間接功耗。如非必須,app要盡量保持閑置,不要推送消息或用其他方式喚醒設(shè)備,特別是app在后臺(tái)的時(shí)候。
網(wǎng)絡(luò)操作。大多數(shù)app都需要網(wǎng)絡(luò)操作。網(wǎng)絡(luò)通信時(shí),蜂窩數(shù)據(jù)和Wi-Fi等元器件開(kāi)始工作就會(huì)消耗電能。分批傳輸、減少傳輸、壓縮數(shù)據(jù)、恰當(dāng)?shù)靥幚礤e(cuò)誤,你的app省電效果會(huì)很顯著。
圖像、動(dòng)畫(huà)、視頻。app內(nèi)容每次更新到屏幕上都需要消耗電能處理像素信息。動(dòng)畫(huà)和視頻格外耗電。不經(jīng)意的或者不必要的內(nèi)容更新同樣會(huì)消耗電能,所以UI不可見(jiàn)時(shí),應(yīng)該避免更新其內(nèi)容。
位置。很多app為了記錄用戶的活動(dòng)或者提供基于位置的服務(wù)會(huì)進(jìn)行定位。定位精度越高,定位時(shí)間越長(zhǎng),消耗電量也就越多。所以app應(yīng)該盡量降低定位精度、縮短定位時(shí)間。不需要位置信息之后立即停止定位。
動(dòng)作傳感器。長(zhǎng)時(shí)間用不上加速度計(jì)、陀螺儀、磁力計(jì)等設(shè)備的動(dòng)作數(shù)據(jù)時(shí),應(yīng)該停止更新數(shù)據(jù),不然也會(huì)浪費(fèi)電能。應(yīng)按需獲取,用完即停。
藍(lán)牙。藍(lán)牙活動(dòng)頻度太高會(huì)消耗電能,應(yīng)該盡量分批、減少數(shù)據(jù)輪詢等操作。
CPU使用量和能耗
CPU使用代價(jià)很大。CPU使用量越大,功率越大,電能消耗越多,電池消耗也就越快。功率大小由于設(shè)備、處理器、其他硬件資源等會(huì)有所不同,表1基于閑置狀態(tài)(idle state),給出了不同CPU使用量的一個(gè)大致的對(duì)比。
表1CPU電量消耗
固定能耗(Fixed Cost)和動(dòng)態(tài)能耗(Dynamic Cost)
iOS設(shè)備閑置時(shí)很擅長(zhǎng)進(jìn)入低功耗狀態(tài)。即使是在兩次鍵盤(pán)敲擊的間隙這種毫秒級(jí)范圍內(nèi),系統(tǒng)閑置時(shí)都能關(guān)掉很多硬件資源。
閑置時(shí)功率消耗很低,對(duì)功耗的影響很小。一旦開(kāi)始執(zhí)行任務(wù),就會(huì)使用系統(tǒng)資源,這些系統(tǒng)資源都需要電能。但是,當(dāng)設(shè)備什么都不干時(shí),零散的任務(wù)會(huì)讓設(shè)備進(jìn)入一種中間狀態(tài),既不是閑置也不是活躍狀態(tài)。在下個(gè)任務(wù)執(zhí)行執(zhí)行前,設(shè)備沒(méi)有足夠的時(shí)間從這些中間狀態(tài)進(jìn)入絕對(duì)閑置狀態(tài)。這種情況下,電能就被白白浪費(fèi)了。
我們app執(zhí)行任務(wù)有動(dòng)態(tài)能耗(dynamic cost)和固定能耗(fixed cost)。動(dòng)態(tài)能耗也就是app實(shí)際工作消耗的能量。固定能耗是在任務(wù)執(zhí)行前后把系統(tǒng)和各種資源調(diào)用起來(lái)和關(guān)閉所消耗的能量。出現(xiàn)大量零散的工作時(shí),在零散的任務(wù)之間因?yàn)橘Y源可能永遠(yuǎn)沒(méi)法真正變?yōu)殚e置,所以有動(dòng)態(tài)能耗的同時(shí)固定能耗也很高。這種情況導(dǎo)致大量電能在相對(duì)較小的實(shí)際工作中流失了,如圖1所示。
圖1 固定和動(dòng)態(tài)能耗
為減少固定能耗交換動(dòng)態(tài)能耗
你的app可以通過(guò)分批執(zhí)行、降低執(zhí)行頻率來(lái)避免產(chǎn)生零散的任務(wù)。例如,不采用同一個(gè)線程串行執(zhí)行一系列任務(wù),而是把任務(wù)同時(shí)放到多個(gè)線程,如圖2所示。每次使用CPU,內(nèi)存、緩存、總線等都得通電。通過(guò)分批執(zhí)行,組件可以只上一次,使用時(shí)間也更短。
圖2 用多線程交換功率
因?yàn)榻o定時(shí)間內(nèi)做了更多的工作,需要更多能量,這種策略會(huì)導(dǎo)致更大的前期動(dòng)態(tài)功耗。作為交換,固定能耗減小了,隨著時(shí)間推移,這會(huì)極大地節(jié)省電能。你的app提高了功率,但它更高效,用了更少的時(shí)間。這使得CPU回到閑置狀態(tài),其他元件也更快地?cái)嚯姟?/p>
當(dāng)開(kāi)發(fā)app時(shí),整個(gè)項(xiàng)目中應(yīng)該全面地思考這種行為,盡量降低固定能耗。
簡(jiǎn)化、有序地工作
減少后臺(tái)工作
實(shí)現(xiàn)UIApplicationDelegate中的方法,應(yīng)用進(jìn)入后臺(tái)前做好暫停任務(wù),保存數(shù)據(jù)等工作。如果確實(shí)需要完成用戶執(zhí)行的一些任務(wù),應(yīng)該調(diào)用UIApplicationDelegate中的beginBackgroundTaskWithExpirationHandler: 方法,這樣后臺(tái)任務(wù)可以繼續(xù)執(zhí)行幾分鐘。任務(wù)執(zhí)行完畢后一定要調(diào)用endBackgroundTask:方法,不要等著系統(tǒng)強(qiáng)行掛起進(jìn)程。
iOS8之后,系統(tǒng)引入了CPU監(jiān)控機(jī)制,以觀察后臺(tái)app的CPU使用量是否超過(guò)了限制,如果超出限制,進(jìn)程可能會(huì)被關(guān)閉。大多數(shù)情況下正常的后臺(tái)任務(wù)不會(huì)遇到這種情況,如果遇到了可以查看崩潰日志信息,異常類型為EXC_RESOURCE,子類型為CPU_FATAL。
用QoS分級(jí)有序工作
多個(gè)app和眾多操作需要共享CPU、緩存、網(wǎng)絡(luò)等資源,為了保持高效,系統(tǒng)需要根據(jù)不同任務(wù)的優(yōu)先級(jí)智能地管理這些工作。比如更新UI這種重要的事需要多分配資源,而一些后臺(tái)任務(wù)可以延遲一些執(zhí)行。服務(wù)質(zhì)量(quality of service, 以下簡(jiǎn)稱QoS, iOS8引入)級(jí)別可以通過(guò)NSOperation, NSOperationQueue, NSThread objects, dispatch queues, 和pthreads (POSIX threads)指定工作的優(yōu)先級(jí)。有4種QoS級(jí)別(和2種特殊的級(jí)別),如表2所示,劃線的兩個(gè)特殊的級(jí)別一般不應(yīng)該使用,僅作了解即可。最好情況是,用戶不交互的時(shí)候,90%以上的時(shí)間讓app運(yùn)行在Utility或更低級(jí)別。
表2QoS優(yōu)先級(jí)
原來(lái)GCD中的全局并發(fā)隊(duì)列(global_queue)用高、默認(rèn)、低、后臺(tái)來(lái)指定隊(duì)列的優(yōu)先級(jí)。現(xiàn)在應(yīng)該改用QoS,兩者的對(duì)應(yīng)關(guān)系如表3。
表3 GCD全局隊(duì)列和QoS對(duì)應(yīng)關(guān)系
少使用定時(shí)器
app經(jīng)常濫用定時(shí)器。想一下你app中的定時(shí)器,是否真的有必要存在。拋開(kāi)具體場(chǎng)景不說(shuō),如果定時(shí)器觸發(fā)太頻繁,能耗影響是比較大的。
用事件通知代替定時(shí)器。有些app用定時(shí)器監(jiān)控文件內(nèi)容、網(wǎng)絡(luò)或者其他狀態(tài)的變化,這會(huì)導(dǎo)致CPU無(wú)法進(jìn)入閑置狀態(tài)而增加功耗。建議使用事件通知來(lái)代替定時(shí)器,比如使用dispatch source監(jiān)測(cè)文件變化。對(duì)于系統(tǒng)提供的服務(wù),盡量使用事件通知,表4是常見(jiàn)的系統(tǒng)通知和對(duì)應(yīng)的監(jiān)測(cè)方法。
表4 系統(tǒng)服務(wù)事件通知
GCD里的dispatch queues、dispatch semaphores等同步工具比定時(shí)器效率高很多,盡量不要用定時(shí)器做同步工具。
所有需要指定一個(gè)最后期限的函數(shù)或方法都屬于定時(shí)器,比如:
1. 高級(jí)定時(shí)器包括dispatch timer sources、CFRunLoopTimerCreate和其他CFRunLoopTimer函數(shù)、NSTimer、performSelector:withObject:afterDelay:方法。
2. 底層定時(shí)器包括sleep, usleep, nanosleep, pthread_cond_timedwait, select, poll, kevent, dispatch_after, dispatch_semaphore_wait。
如果一定要用定時(shí)器,盡量高效地使用,可以參照下列指導(dǎo)方針:
1. 設(shè)置一個(gè)合適的超時(shí)時(shí)間。
2. 不再需要時(shí)及時(shí)關(guān)閉重復(fù)性定時(shí)器。
3. 設(shè)置觸發(fā)公差。
優(yōu)化I/O訪問(wèn)
app每次執(zhí)行I/O任務(wù),比如寫(xiě)文件,會(huì)導(dǎo)致系統(tǒng)退出閑置模式。而且寫(xiě)入緩存格外耗電。通過(guò)下列方法可以提高能效、改善app性能。
1. 減小寫(xiě)入數(shù)據(jù)。數(shù)據(jù)有變化再寫(xiě)文件,盡量把多個(gè)更改攢到一起一次性寫(xiě)入。如果只有幾個(gè)字節(jié)的數(shù)據(jù)改變,不要把整個(gè)文件重新寫(xiě)入一次。如果你的app經(jīng)常要修改大文件里很少的內(nèi)容,可以考慮用數(shù)據(jù)庫(kù)存儲(chǔ)這些數(shù)據(jù)。
2. 避免訪問(wèn)存儲(chǔ)頻度太高。如果app要存儲(chǔ)狀態(tài)信息,要等到狀態(tài)信息有變化時(shí)再寫(xiě)入。盡量分批修改,不要頻繁地寫(xiě)入這些小變動(dòng)。
3. 盡量順序讀寫(xiě)數(shù)據(jù)。在文件中跳轉(zhuǎn)位置會(huì)消耗一些時(shí)間。
4. 盡量從文件讀寫(xiě)大數(shù)據(jù)塊,一次讀取太多數(shù)據(jù)可能會(huì)引發(fā)一些問(wèn)題。比如,讀取一個(gè)32M文件的全部?jī)?nèi)容可能會(huì)在讀取完成前觸發(fā)內(nèi)容分頁(yè)。
5. 讀寫(xiě)大量重要數(shù)據(jù)時(shí),考慮用dispatch_io,其提供了基于GCD的異步操作文件I/O的API。用dispatch_io系統(tǒng)會(huì)優(yōu)化磁盤(pán)訪問(wèn)。
6. 如果你的數(shù)據(jù)由隨機(jī)訪問(wèn)的結(jié)構(gòu)化內(nèi)容組成,建議將其存儲(chǔ)在數(shù)據(jù)庫(kù)中,可以使用SQLite或Core Data訪問(wèn)。特別是需要操作的內(nèi)容可能增長(zhǎng)到超過(guò)幾兆的時(shí)候。
7. 了解系統(tǒng)如何緩存文件、如何優(yōu)化緩存的使用。如果你不打算多次引用某些數(shù)據(jù),不要自己緩存數(shù)據(jù)。
低電量模式
iOS9之后,iPhone增加了低電量模式,用戶如果希望延長(zhǎng)iPhone電池的壽命,可以在設(shè)置 > 電池中開(kāi)啟該功能。開(kāi)啟該功能之后iOS會(huì)采取一些措施,比如:
1. 降低CPU和GPU性能
2. 暫停隨意的和后臺(tái)的活動(dòng),包括網(wǎng)絡(luò)
3. 降低屏幕亮度
4. 縮短自動(dòng)鎖屏?xí)r間
5. 關(guān)閉郵件刷新
6. 關(guān)閉視角縮放
7. 關(guān)閉動(dòng)態(tài)壁紙
你的app也應(yīng)該做一些事情幫助系統(tǒng)節(jié)省電能,比如,可以減少動(dòng)畫(huà)、降低幀率、停止位置更新、關(guān)閉同步和備份功能等等。可以通過(guò)向NSNotificationCenter注冊(cè)NSProcessInfoPowerStateDidChangeNotification通知監(jiān)聽(tīng)低電量模式狀態(tài)。
網(wǎng)絡(luò)操作
只要app一執(zhí)行網(wǎng)絡(luò)操作,就會(huì)產(chǎn)生大量間接能耗(overhead cost)。網(wǎng)絡(luò)硬件,比如蜂窩數(shù)據(jù)和Wi-Fi電路,為了省電默認(rèn)是不通電的。為了執(zhí)行網(wǎng)絡(luò)操作,這些資源必須通電,之后為了等待接下來(lái)可能出現(xiàn)的任務(wù),它們?cè)诓僮魍瓿珊髸?huì)繼續(xù)保持一段時(shí)間的活躍。零散的網(wǎng)絡(luò)傳輸會(huì)導(dǎo)致很高的間接能耗,迅速消耗電池電量,如圖3所示。
圖3 多次網(wǎng)絡(luò)活動(dòng)產(chǎn)生間接能耗
縮減網(wǎng)絡(luò)請(qǐng)求
1. 減少、壓縮網(wǎng)絡(luò)數(shù)據(jù)。可以降低上傳或下載的多媒體內(nèi)容質(zhì)量和尺寸等。
2. 使用緩存,不要重復(fù)下載相同的數(shù)據(jù)。
3. 使用斷點(diǎn)續(xù)傳,否則網(wǎng)絡(luò)不穩(wěn)定時(shí)可能多次傳輸相同的內(nèi)容。
4. 網(wǎng)絡(luò)不可用時(shí)不要嘗試執(zhí)行網(wǎng)絡(luò)請(qǐng)求,盡量只在Wi-Fi情況下聯(lián)網(wǎng)。
5. 讓用戶可以取消長(zhǎng)時(shí)間運(yùn)行或者速度很慢的網(wǎng)絡(luò)操作,設(shè)置合適的超時(shí)時(shí)間。
6. 網(wǎng)絡(luò)請(qǐng)求失敗后用SCNetworkReachability的通知監(jiān)測(cè)網(wǎng)絡(luò)狀態(tài),網(wǎng)絡(luò)可用后再重試。
延遲聯(lián)網(wǎng)
分批傳輸。比如,下載視頻流時(shí),不要傳輸很小的數(shù)據(jù)包,直接下載整個(gè)文件或者一大塊一大塊地下載。如果提供廣告,一次性多下載一些,然后再慢慢展示。如果要從服務(wù)器下載電子郵件,一次下載多條,不要一條一條地下載。
網(wǎng)絡(luò)操作能推遲就推遲。如果通過(guò)HTTP上傳、下載數(shù)據(jù),建議使用NSURLSession中的后臺(tái)會(huì)話,這樣系統(tǒng)可以針對(duì)整個(gè)設(shè)備所有的網(wǎng)絡(luò)操作優(yōu)化功耗。將可以推遲的操作盡量推遲到設(shè)備充電狀態(tài)并且連接Wi-Fi時(shí)進(jìn)行,比如同步和備份工作。
VoIP類應(yīng)用應(yīng)該用PushKit而不是長(zhǎng)連接。
圖像、動(dòng)畫(huà)、視頻
以下列指導(dǎo)方針優(yōu)化內(nèi)容更新:
1. 減少app使用的視圖數(shù)量。
2. 減少不透明視圖的使用,比如視圖上顯示一個(gè)半透明模糊效果。如果要用不透明效果,避免用在內(nèi)容頻繁變化的地方。另外,由于內(nèi)容變化后背景視圖和半透明視圖必須同時(shí)改變,這也會(huì)放大功耗。
3. 避免繪制不可見(jiàn)的內(nèi)容,比如app的內(nèi)容被其他視圖遮擋、被剪切(clipped)或者出畫(huà)了。
4. 動(dòng)畫(huà)盡可能用較低的幀率。比如,高幀率在玩游戲時(shí)有意義,但是菜單畫(huà)面可能較低的幀率就夠了。只有對(duì)用戶體驗(yàn)有影響時(shí)才使用高幀率。
5. 執(zhí)行動(dòng)畫(huà)時(shí)不要修改幀率。比如,你的app幀率是60fps,整個(gè)動(dòng)畫(huà)就保持這個(gè)幀率不要變。
6. 避免同時(shí)在屏幕上使用多種幀率。比如,你的游戲人物是60fps,天上的云彩移動(dòng)又是30fps,不要出現(xiàn)這種狀況,就算提高其中某一個(gè)的幀率,也要用相同的幀率。
7. 開(kāi)發(fā)游戲時(shí)使用推薦的framework。這些framework針對(duì)性能和功耗是做過(guò)優(yōu)化的:2D游戲用SpriteKit、3D游戲用SceneKit、畫(huà)面非常逼真的游戲用Mietal。
全屏播放視頻時(shí)iOS可以通過(guò)高效管理各種資源來(lái)優(yōu)化能耗,但是在視頻上下額外添加圖層會(huì)影響功耗優(yōu)化效果。app盡量不要在全屏視頻上添加額外的圖層(即使是隱藏的圖層)。如果用戶有需要,可以通過(guò)比如單擊這樣的方式來(lái)顯示播放控制之類的UI,不需要了以后應(yīng)該把這些圖層移除掉。
優(yōu)化定位和動(dòng)作(Motion)
錯(cuò)誤使用定位會(huì)阻礙設(shè)備進(jìn)入睡眠模式,讓定位硬件部分持續(xù)通電而消耗電池電量,這會(huì)使用戶體驗(yàn)變的很差,下面來(lái)看一下如何針對(duì)功耗優(yōu)化定位服務(wù)。
如果你的app只是需要快速確定一下用戶的位置,最好用CLLocationManager的requestLocation (iOS9引入)方法。定位完成之后會(huì)自動(dòng)讓硬件斷電。
除了導(dǎo)航,大多數(shù)app不需要一直實(shí)時(shí)更新位置。需要位置服務(wù)時(shí)開(kāi)啟一下定位,盡量多隔一些時(shí)間再進(jìn)行下次位置更新,更新完了之后馬上關(guān)掉定位。除非用戶在移動(dòng)的交通工具里,否則不頻繁地更新位置一般沒(méi)多大問(wèn)題。
盡量降低定位精度。iOS設(shè)備默認(rèn)采用最高精度定位,如果你的app不是確實(shí)需要米級(jí)的位置信息,不要用最高精度(kCLLocationAccuracyBest)或10米左右的精度(kCLLocationAccuracyNearestTenMeters)。一般來(lái)說(shuō)Core Location提供的精度比你設(shè)置的要好,比如你設(shè)置為3公里左右的精度,可能會(huì)收到100米左右的精度信息。
如果定位精度一直達(dá)不到設(shè)置的精度時(shí),停止更新位置,稍后再試。
需要后臺(tái)更新位置時(shí),盡量把pausesLocationUpdatesAutomatically設(shè)為YES,如果用戶不太可能移動(dòng)的時(shí)候系統(tǒng)會(huì)自動(dòng)暫停位置更新。
后臺(tái)定位時(shí)延時(shí)更新位置。如果要做一個(gè)健身類的軟件追蹤用戶徒步的距離,可以等用戶移動(dòng)一段距離或者過(guò)一段時(shí)間之后再更新位置,這樣可以讓系統(tǒng)優(yōu)化能耗。
合理使用訪問(wèn)監(jiān)控(visit monitoring)。訪問(wèn)監(jiān)控允許app接收用戶頻繁或長(zhǎng)時(shí)間訪問(wèn)的場(chǎng)所的進(jìn)出通知,比如在家、公司或者去喜歡的咖啡館。
盡量不要用significant-change位置服務(wù),優(yōu)先考慮用region monitoring、 visit monitoring。
用戶移動(dòng)、搖晃、傾斜設(shè)備時(shí),會(huì)產(chǎn)生動(dòng)作(motion)事件,這些事件由加速度計(jì)、陀螺儀、磁力計(jì)等硬件檢測(cè)。不需要監(jiān)測(cè)設(shè)備方向時(shí)停止通知。比如用戶進(jìn)入一個(gè)只需要豎著顯示的畫(huà)面,及時(shí)把方向改變通知關(guān)掉。開(kāi)啟動(dòng)作事件前設(shè)置一個(gè)比較大的更新間隔。
優(yōu)化通知
盡量用本地通知(local notification),如果你的app不依賴外部數(shù)據(jù),而是需要基于時(shí)間的通知,應(yīng)該用本地通知,可以讓設(shè)備的網(wǎng)絡(luò)硬件休息一下。
遠(yuǎn)程推送有兩個(gè)級(jí)別,一個(gè)是立即推送,另一個(gè)是針對(duì)功耗優(yōu)化過(guò)的延時(shí)推送。如果不是真的需要即時(shí)推送,盡量使用延時(shí)推送。
優(yōu)化藍(lán)牙通信
1. 沒(méi)有必要的時(shí)候不要掃描藍(lán)牙外設(shè)。
2. 掃描外設(shè)時(shí)一般不要用CBCentralManagerScanOptionAllowDuplicatesKey。
3. 只查找你需要的外設(shè)服務(wù)。外設(shè)可能提供很多服務(wù)和特性(characteristic),查找外設(shè)的時(shí)候可以指定UUID。
4. 不要輪詢?cè)O(shè)備特性值,用通知監(jiān)測(cè)特征值的變化。
5. 特性值不再提供通知或者不再需要通信的時(shí)候就斷開(kāi)連接。
Apple Watch
Apple Watch有很多節(jié)能的特征,而且watchOS的API也非常高效。但是仍然需要堅(jiān)持下面的指導(dǎo)方針:
1. 減少iPhone和手表之間的通信,分批通信,用NSURLSession的后臺(tái)會(huì)話延遲聯(lián)網(wǎng)。
2. 去掉不必要的內(nèi)容刷新。
3. 盡量用暗色,亮色會(huì)顯著增加功耗。除了省電,暗色還可以讓屏幕邊框和顯示內(nèi)容融合得更好
4. 縮小媒體數(shù)據(jù)大小。如果你的app需要從服務(wù)器下載圖片,下載適合手表屏幕尺寸的圖片,不要下載大圖再縮放,這樣網(wǎng)絡(luò)和CPU功耗都更高。
5. 少做工作。如果app需要復(fù)雜或者大量的處理任務(wù),考慮將其發(fā)送給iPhone處理。
監(jiān)測(cè)功耗
測(cè)試或者debug你的app時(shí),注意下列情況:
電池消耗過(guò)快
app應(yīng)該閑置時(shí)卻活動(dòng)
響應(yīng)慢,UI卡頓
主線程執(zhí)行大量任務(wù)
動(dòng)畫(huà)使用過(guò)多
不透明視圖過(guò)多
切換應(yīng)用
內(nèi)存慢,沒(méi)有緩存(Memory stalls and cache misses)
內(nèi)存警告
Lock contention
頻繁地切換context
過(guò)度使用定時(shí)器
頻繁繪制屏幕
頻繁或者反復(fù)執(zhí)行很小數(shù)據(jù)的I/O操作
很高的通信間接功耗,比如傳輸零散的小數(shù)據(jù)包和緩沖
設(shè)備不休眠
用Xcode測(cè)量功耗
開(kāi)發(fā)app的過(guò)程中是診斷能耗最好的時(shí)機(jī)。在Xcode中選擇View > Navigators > Show Debug Navigator,這里提供了很多儀表用于分析功耗。Energy impact可以查看正在運(yùn)行的app的功耗,如圖4。
圖4 Xcode中的功耗儀表
Cost 和 overhead。藍(lán)色的是CPU執(zhí)行任務(wù)消耗的電量,紅色的是執(zhí)行你的app消耗的其他系統(tǒng)資源電能。
CPU。灰色方塊表示你的app正在使用CPU執(zhí)行任務(wù)。
Network。灰色方塊表示你的app正在進(jìn)行網(wǎng)絡(luò)操作。
Location。灰色方塊表示你的app正在使用位置服務(wù)。
GPU。灰色方塊表示你的app正在使用GPU執(zhí)行圖像相關(guān)操作,比如繪圖或者播放動(dòng)畫(huà)。
Background。灰色方塊表示你的app處于后臺(tái)狀態(tài),但是讓系統(tǒng)仍然保持喚醒狀態(tài)。
和用戶交互時(shí)功耗應(yīng)該比用戶選擇一個(gè)復(fù)雜的操作時(shí)低,不交互時(shí)不應(yīng)該有功耗。
使用Instruments之前應(yīng)該先考慮用Xcode中的儀表檢查功耗問(wèn)題。
用Instruments檢測(cè)功耗
1. 啟動(dòng)Instruments,選擇你的設(shè)備和要檢測(cè)的app,打開(kāi)Energy Log,如圖5。推薦使用無(wú)線方式連接設(shè)備,這樣可以完全模擬使用電池工作的真實(shí)場(chǎng)景。將設(shè)備和Mac用數(shù)據(jù)線連接好,在圖5頁(yè)面按住?選擇設(shè)備,會(huì)出現(xiàn)帶有Wireless后綴的設(shè)備。
圖5 啟動(dòng)Instruments
2. 點(diǎn)擊Record按鈕或者按?+R,開(kāi)始記錄。
3. 在設(shè)備上正常使用app,這時(shí)會(huì)記錄功耗數(shù)據(jù)。
4. 點(diǎn)擊Stop按鈕或按?+R,完成記錄。
查看記錄的數(shù)據(jù)有沒(méi)有異常或者可以優(yōu)化的地方,如圖6。
圖6 用Energy Log查看功耗相關(guān)記錄
提示:app能耗偶爾比較高不一定是app的問(wèn)題,可能當(dāng)時(shí)的操作本身就很耗電,比如說(shuō)執(zhí)行網(wǎng)絡(luò)操作的時(shí)候使用GPS。你應(yīng)該關(guān)注的是峰值、出乎意料的高功耗區(qū)域和其他可以優(yōu)化的地方。
用iOS設(shè)備直接記錄功耗
不用有線或無(wú)線方式連接Instruments,直接用iOS設(shè)備記錄功耗可以獲得更真實(shí)的數(shù)據(jù)。記錄工作幾乎不耗電,可以全天候使用,即使設(shè)備進(jìn)入睡眠模式也會(huì)持續(xù)記錄。但是如果設(shè)備關(guān)機(jī)后,數(shù)據(jù)可能會(huì)丟失。
在設(shè)備上進(jìn)入設(shè)置 > 開(kāi)發(fā)者 > Logging.
開(kāi)啟功耗記錄,如圖7。
圖7 開(kāi)啟功耗記錄
點(diǎn)擊Start Recording按鈕。
正常使用設(shè)備。
設(shè)備記錄完成后返回圖6所示頁(yè)面,點(diǎn)擊Stop Recording.
在Instruments中選擇好設(shè)備,進(jìn)入Energy Log.
選擇File > Import Logged Data from Device。
使用其他模板和儀器檢測(cè)功耗
有很多因素會(huì)影響app的功耗,Engergy Log這個(gè)模板可以分析一部分因素,你還可以用其他的模板或工具檢查app對(duì)功耗的影響。
Activity Monitor. 查看CPU、I/O、網(wǎng)絡(luò)使用。
Core Animation. 測(cè)量圖像性能和CPU使用量。
GPU Driver. 測(cè)量GPU驅(qū)動(dòng)數(shù)據(jù)。
Location Energy Impact. 測(cè)量Core Location對(duì)能耗的影響。
Metal System Trace. 通過(guò)追蹤app、驅(qū)動(dòng)、GPU的數(shù)據(jù),檢測(cè)iOS Metal應(yīng)用的性能。
Network. 分析TCP/IP 和UDP/IP 連接。
Time Profiler. 該工具檢測(cè)app正在運(yùn)行的線程,隔一段時(shí)間采樣一次。每個(gè)采樣都有完整的調(diào)用棧(backtrace),你可以找出你的代碼中哪里耗費(fèi)了大量時(shí)間。
自定義模板。上面的模板或儀器可以分析app的多個(gè)方面。如果你想查看特定的幾個(gè)方面,可以向Instruments中添加單個(gè)的儀器。如果以后可能還要用相同的分析類型,可以把你配置的工具保存成模板。
測(cè)試性能
app的性能下降會(huì)導(dǎo)致功耗增加,可以用Xcode中的XCTest框架測(cè)試你的代碼。代碼會(huì)在性能測(cè)試的block中連續(xù)運(yùn)行10次,并給出運(yùn)行平均時(shí)間的標(biāo)準(zhǔn)差。