當(dāng)用戶不太想用你的應(yīng)用的時(shí)候膏斤,系統(tǒng)會(huì)讓它進(jìn)入后臺(tái)狀態(tài)彻舰。對(duì)于很多應(yīng)用來(lái)說(shuō),后臺(tái)狀態(tài)只是進(jìn)入掛起(suspended)狀態(tài)過(guò)程中一個(gè)短暫的瞬間诫咱。掛起應(yīng)用是提高電池壽命的一種方式笙隙,它還允許系統(tǒng)將重要的系統(tǒng)資源分配給新的引起用戶注意的前臺(tái)應(yīng)用。
大多數(shù)應(yīng)用進(jìn)入到掛起狀態(tài)很容易坎缭,但還是有一些讓應(yīng)用繼續(xù)在后臺(tái)運(yùn)行的合理理由竟痰。一個(gè)徒步應(yīng)用或許希望能全程跟蹤用戶的位置,這樣它就能顯示覆蓋在徒步地圖上的行程掏呼。一個(gè)音樂應(yīng)用或許希望能在鎖屏的時(shí)候繼續(xù)播放音樂坏快。其他一些應(yīng)用或許希望在后臺(tái)下載內(nèi)容逝变,這樣它就能以最小的延時(shí)來(lái)向用戶呈現(xiàn)內(nèi)容骄噪。當(dāng)你發(fā)現(xiàn)有必要讓你的應(yīng)用運(yùn)行在后臺(tái)的時(shí)候,iOS會(huì)高效的幫你做到拜轨,并且不會(huì)浪費(fèi)系統(tǒng)資源或者用戶的電量拾给。iOS提供的技術(shù)分為三類:
- 在前臺(tái)啟動(dòng)一個(gè)簡(jiǎn)短任務(wù)的應(yīng)用祥得,能在應(yīng)用進(jìn)入后臺(tái)的時(shí)候請(qǐng)求一點(diǎn)時(shí)間來(lái)完成這個(gè)任務(wù)。
- 在前臺(tái)啟動(dòng)下載任務(wù)的應(yīng)用蒋得,能把這些下載任務(wù)的管理權(quán)交給系統(tǒng)级及,這樣,就能在下載期間允許應(yīng)用被掛起或者終止窄锅。
- 需要運(yùn)行在后臺(tái)來(lái)支持特定類型的任務(wù)的應(yīng)用,可以聲明對(duì)一個(gè)或多個(gè)后臺(tái)執(zhí)行模式的支持缰雇。
要盡量避免在后臺(tái)工作入偷,除非所作的事情有利于改善用戶的綜合體驗(yàn)。應(yīng)用進(jìn)入后臺(tái)的原因可能是用戶啟動(dòng)了另一個(gè)應(yīng)用械哟、或者因?yàn)橛脩翩i定了設(shè)備并且現(xiàn)在它沒有在運(yùn)行疏之。在這兩種情況下,用戶都表明現(xiàn)在你的應(yīng)用不需要做任何有意義的事暇咆。在這種前提下锋爪,繼續(xù)運(yùn)行只會(huì)浪費(fèi)設(shè)備的電量丙曙,并有可能導(dǎo)致用戶強(qiáng)制退出一的應(yīng)用。所以要注意你在后臺(tái)所作的事其骄,盡可能避免這種情況亏镰。
執(zhí)行有限長(zhǎng)度的任務(wù)
進(jìn)入后臺(tái)的應(yīng)用被期望能盡快的進(jìn)入休眠狀態(tài),一遍它們能被系統(tǒng)掛起拯爽。如果你的應(yīng)用正在任務(wù)中索抓,并且需要一點(diǎn)額外的事件來(lái)完成任務(wù),它可以調(diào)用UIApplication對(duì)象的beginBackgroundTaskWithName:expirationHandler: 或beginBackgroundTaskWithExpirationHandler:方法來(lái)請(qǐng)求一些額外的執(zhí)行時(shí)間毯炮。調(diào)用這些方法可以臨時(shí)推遲應(yīng)用的掛起逼肯,給完成任務(wù)一點(diǎn)額外的時(shí)間。完成這項(xiàng)任務(wù)后桃煎,應(yīng)用必須調(diào)用endBackgroundTask: 方法來(lái)讓系統(tǒng)知道任務(wù)已完成可以被掛起了篮幢。
每次調(diào)用beginBackgroundTaskWithName:expirationHandler:還是 beginBackgroundTaskWithExpirationHandler:方法都會(huì)產(chǎn)生一個(gè)與相應(yīng)任務(wù)關(guān)聯(lián)的唯一標(biāo)記(token)。當(dāng)你的應(yīng)用完成任務(wù)的時(shí)候为迈,它必須使用相應(yīng)的標(biāo)記調(diào)用 endBackgroundTask:方法三椿,以便讓系統(tǒng)知道這個(gè)任務(wù)完成了。如果為后臺(tái)任務(wù)調(diào)用endBackgroundTask:失敗將會(huì)導(dǎo)致應(yīng)用終止曲尸。如果在開始這個(gè)任務(wù)的時(shí)候你提供一個(gè)到器處理器(handler)赋续,系統(tǒng)會(huì)調(diào)用這個(gè)處理器,并給你最后一個(gè)結(jié)束任務(wù)以及避免應(yīng)用終止的機(jī)會(huì)另患。
你不需要等到應(yīng)用進(jìn)入后臺(tái)之后再來(lái)指定后臺(tái)任務(wù)纽乱。更有用的設(shè)計(jì)是在開始任務(wù)之前調(diào)用beginBackgroundTaskWithName:expirationHandler:或beginBackgroundTaskWithExpirationHandler:方法,并且一旦你完成就調(diào)用endBackgroundTask:方法昆箕。你甚至可以在應(yīng)用還在前臺(tái)執(zhí)行的時(shí)候遵循這個(gè)模式鸦列。
代碼清單3-1展示了當(dāng)應(yīng)用轉(zhuǎn)換到后臺(tái)的時(shí)候如何開始一個(gè)長(zhǎng)時(shí)運(yùn)行任務(wù)。在這個(gè)例子中鹏倘,啟動(dòng)后臺(tái)任務(wù)的請(qǐng)求包含了一個(gè)到期處理器薯嗤,它的任務(wù)就是防治任務(wù)耗時(shí)過(guò)長(zhǎng)。然后任務(wù)自身被提交給調(diào)度隊(duì)列進(jìn)行異步執(zhí)行纤泵,所以applicationDidEnterBackground:方法能夠正常返回骆姐。代碼塊(block)的使用精簡(jiǎn)了維持對(duì)重要變量引用所需的代碼,例如后臺(tái)任務(wù)的標(biāo)識(shí)符捏题。清單中的bgTask變量是存儲(chǔ)當(dāng)前后臺(tái)任務(wù)標(biāo)識(shí)符的類的成員變量玻褪,并且被此方法使用之前已初始化。
代碼清單3-1 在退出的時(shí)候啟動(dòng)后臺(tái)任務(wù)
- (void)applicationDidEnterBackground:(UIApplication *)application
{
bgTask = [application beginBackgroundTaskWithName:@"MyTask" expirationHandler:^{
// Clean up any unfinished task business by marking where you
// stopped or ending the task outright.
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
// Start the long-running task and return immediately.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Do the work associated with the task, preferably in chunks.
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
});
}
注意:當(dāng)啟動(dòng)任務(wù)的時(shí)候公荧,總是提供一個(gè)到期處理器带射,但如果你想要知道應(yīng)用運(yùn)行了多長(zhǎng)時(shí)間,請(qǐng)獲取UIApplication的backgroundTimeRemaining屬性的值循狰。
在你自己的到期處理器中窟社,你可以包含必要的額外代碼來(lái)關(guān)閉你的任務(wù)券勺。但是,任何你飽含的代碼都不能花費(fèi)太長(zhǎng)時(shí)執(zhí)行時(shí)間灿里,因?yàn)楣亓叮诘狡谔幚砥鞅徽{(diào)用的時(shí)候,應(yīng)用已經(jīng)非常接近它的限定時(shí)間了钠四。因此盗扒,僅執(zhí)行最少的狀態(tài)信息清理,并結(jié)束任務(wù)缀去。
在后臺(tái)下載內(nèi)容
當(dāng)下載文件時(shí)侣灶,應(yīng)用應(yīng)該使用NSURLSession對(duì)象來(lái)啟動(dòng)下載,這樣系統(tǒng)能夠控制下載過(guò)程缕碎,以防應(yīng)用被掛起或終止褥影。當(dāng)你為后臺(tái)傳輸配置一個(gè)NSURLSession對(duì)象時(shí),系統(tǒng)在獨(dú)立的進(jìn)程里管理這些傳輸咏雌,并且通過(guò)常規(guī)方式返回狀態(tài)報(bào)告給應(yīng)用凡怎。如果應(yīng)用在傳輸過(guò)程中被終止,系統(tǒng)會(huì)在后臺(tái)繼續(xù)這個(gè)傳輸赊抖,并在傳輸結(jié)束或者一個(gè)及多個(gè)任務(wù)需要應(yīng)用注意的時(shí)候啟動(dòng)應(yīng)用(以合適的方式)统倒。
為了支持后臺(tái)傳輸,你必須恰當(dāng)?shù)嘏渲媚愕腘SURLSession對(duì)象氛雪。要配置會(huì)話房匆,你必須先創(chuàng)建一個(gè)NSURLSessionConfiguration對(duì)象,并將幾個(gè)屬性設(shè)置恰當(dāng)?shù)刂当丁H缓笤『瑁谀銊?chuàng)建會(huì)話的時(shí)候,把配置好的對(duì)象傳遞給適當(dāng)?shù)豊SURLSession初始化方法弦追。
創(chuàng)建支持后臺(tái)下載的配置對(duì)象的過(guò)程如下所示:
- 使用NSURLSessionConfiguration的backgroundSessionConfigurationWithIdentifier:方法來(lái)創(chuàng)建配置對(duì)象岳链。
- 將配置對(duì)象的sessionSendsLaunchEvents屬性設(shè)置為YES。
- 如果應(yīng)用在前臺(tái)啟動(dòng)了傳輸劲件,建議將配置對(duì)象的discretionary屬性也設(shè)置為YES掸哑。
- 恰當(dāng)?shù)嘏渲闷渌渲脤?duì)象的其他屬性。
- 使用這個(gè)配置對(duì)象創(chuàng)建NSURLSession對(duì)象零远。
一旦配置完成苗分,你的NSURLSession對(duì)象會(huì)無(wú)縫地在適當(dāng)?shù)臅r(shí)候?qū)⑸蟼骱拖螺d的任務(wù)交給系統(tǒng)。如果在應(yīng)用還在運(yùn)行的時(shí)候任務(wù)完成(無(wú)論是前臺(tái)還是后臺(tái))遍烦,會(huì)話對(duì)象都會(huì)以正常方式通知它的委托對(duì)象俭嘁。如果任務(wù)還沒有完成而系統(tǒng)終止了應(yīng)用躺枕,系統(tǒng)會(huì)自動(dòng)的在后臺(tái)繼續(xù)管理這些任務(wù)服猪。如果用戶終止了應(yīng)用供填,則系統(tǒng)就會(huì)取消任何等待中的任務(wù)。
當(dāng)所有與后臺(tái)會(huì)話關(guān)聯(lián)的任務(wù)完成的時(shí)候罢猪,系統(tǒng)重啟終止的應(yīng)用(假定sessionSendsLaunchEvents屬性被設(shè)置為YES近她,并且用戶沒有強(qiáng)退應(yīng)用),并且調(diào)用應(yīng)用的委托對(duì)象的application:handleEventsForBackgroundURLSession:completionHandler:方法膳帕。(系統(tǒng)還可以重啟應(yīng)用來(lái)處理驗(yàn)證粘捎,或者其他需要應(yīng)用注意的任務(wù)相關(guān)的事件)。在你的委托方法實(shí)現(xiàn)中危彩,使用提供的標(biāo)識(shí)符創(chuàng)建新的與之前配置相同的NSURLSessionConfiguration和NSURLSession對(duì)象攒磨。系統(tǒng)會(huì)將你的心會(huì)話對(duì)象連接到之前的任務(wù),并將它們的狀態(tài)報(bào)告給會(huì)話對(duì)象的委托對(duì)象汤徽。
實(shí)現(xiàn)長(zhǎng)時(shí)運(yùn)行任務(wù)
為了實(shí)現(xiàn)那些請(qǐng)求了更多執(zhí)行時(shí)間的任務(wù)娩缰,你必須請(qǐng)求特定的許可來(lái)讓他們?cè)诤笈_(tái)運(yùn)行而不被掛起。在iOS中谒府,只有特定的幾種應(yīng)用類型才被允許在后臺(tái)運(yùn)行:
- 在后臺(tái)時(shí)向用戶播放音頻內(nèi)容的應(yīng)用拼坎,例如音樂播放應(yīng)用。
- 在后臺(tái)時(shí)錄制音頻內(nèi)容的應(yīng)用完疫。
- 隨時(shí)記錄用戶位置信息的應(yīng)用泰鸡,例如導(dǎo)航應(yīng)用。
- 支持互聯(lián)網(wǎng)語(yǔ)音協(xié)議(Voice over Internet Protocol壳鹤,VoIP)的應(yīng)用盛龄。
- 需要經(jīng)常下載和處理新內(nèi)容的應(yīng)用。
- 從外部配件接收定期更新的應(yīng)用
實(shí)現(xiàn)這些服務(wù)的應(yīng)用必須聲明它們支持這些服務(wù)器虾,并且使用系統(tǒng)框架來(lái)實(shí)現(xiàn)這些服務(wù)的相關(guān)方面讯嫂。聲明服務(wù)讓系統(tǒng)知道你使用了哪個(gè)服務(wù),但在某些情況下兆沙,事實(shí)上阻止你的應(yīng)用被掛起的是系統(tǒng)框架欧芽。
聲明應(yīng)用支持的后臺(tái)任務(wù)
那些后臺(tái)執(zhí)行的類的支持,必須通過(guò)使用它們的類應(yīng)用提前聲明葛圃。從Xcode 5開始千扔,從項(xiàng)目設(shè)置的Capabilities選項(xiàng)卡中聲明你的應(yīng)用支持的后臺(tái)模式。啟用Background Modes選項(xiàng)將UIBackgroundModes鍵添加到到應(yīng)用的Info.plist文件库正。選擇一個(gè)或多個(gè)復(fù)選框來(lái)添加相應(yīng)的后臺(tái)模式值到那個(gè)鍵曲楚。表3-1羅列了你能指定的后臺(tái)模式,以及Xcode分配給應(yīng)用Info.plist 文件中的UIBackgroundModes鍵的值褥符。
表3-1 應(yīng)用的后臺(tái)模式
Xcode后臺(tái)模式 | UIBackgroundModes值 | 描述 |
---|---|---|
Audio and AirPlay | audio | 應(yīng)用向用戶播放聲音內(nèi)容龙誊,或者在后臺(tái)錄制音頻。(這個(gè)內(nèi)容包含使用AirPlay的音頻流或視頻流喷楣。) 用戶必須在第一次使用前允許應(yīng)用使用麥克風(fēng)趟大。更多信息鹤树,參見Supporting User Privacy。 |
Location update | location | 應(yīng)用允許保存用戶的位置信息逊朽,甚至當(dāng)應(yīng)用運(yùn)行在后臺(tái)的時(shí)候罕伯。 |
Voice over IP | voip | 應(yīng)用提供一種讓用戶使用網(wǎng)絡(luò)連接打電話的功能。 |
Newsstand downloads | newsstand-content | 應(yīng)用是Newsstand應(yīng)用叽讳,它在后臺(tái)下載并處理雜志或報(bào)紙的內(nèi)容追他。 |
External accessory communication | external-accessory | 應(yīng)用與需要通過(guò)External Accessory框架提供定期更新的硬件附件配合使用。 |
Uses Bluetooth LE accessories | bluetooth-central | 應(yīng)用與需要通過(guò)Core Bluetooth 框架提供定期更新的藍(lán)牙附件配合使用岛蚤。 |
Acts as a Bluetooth LE accessory | bluetooth-peripheral | 應(yīng)用通過(guò)Core Bluetooth框架支持在外設(shè)模式下的藍(lán)牙通信邑狸。 使用這個(gè)模式需要用戶授權(quán);更多信息參見Supporting User Privacy涤妒。 |
Background fetch | fetch | 應(yīng)用定期從網(wǎng)絡(luò)下載和處理少量?jī)?nèi)容推溃。 |
Remote notifications | remote-notification | 應(yīng)用想要在推送通知到達(dá)的時(shí)候開始下載內(nèi)容。使用通知來(lái)最小化顯示推送通知相關(guān)的內(nèi)容的延時(shí)届腐。 |
以上的每種模式讓系統(tǒng)知道你的應(yīng)用在合適的時(shí)候會(huì)被喚醒或者啟動(dòng)過(guò)以響應(yīng)相關(guān)的事件铁坎。例如,應(yīng)用開始播放音樂犁苏,然后它進(jìn)入后臺(tái)硬萍,但仍然需要執(zhí)行時(shí)間來(lái)填充音頻輸出緩沖器。啟用Audio模式围详,告訴系統(tǒng)框架朴乖,它們應(yīng)該繼續(xù)以適當(dāng)?shù)亻g隔來(lái)進(jìn)行必要的調(diào)用。如果應(yīng)用沒有選擇這個(gè)模式助赞,所有通過(guò)這個(gè)應(yīng)用正在播放或錄制的音頻买羞,在應(yīng)用進(jìn)入后臺(tái)的時(shí)候都會(huì)停止。
(未完待續(xù)......)