當用戶不太想用你的應用的時候,系統會讓它進入后臺狀態。對于很多應用來說,后臺狀態只是進入掛起(suspended)狀態過程中一個短暫的瞬間。掛起應用是提高電池壽命的一種方式,它還允許系統將重要的系統資源分配給新的引起用戶注意的前臺應用。
大多數應用進入到掛起狀態很容易,但還是有一些讓應用繼續在后臺運行的合理理由。一個徒步應用或許希望能全程跟蹤用戶的位置,這樣它就能顯示覆蓋在徒步地圖上的行程。一個音樂應用或許希望能在鎖屏的時候繼續播放音樂。其他一些應用或許希望在后臺下載內容,這樣它就能以最小的延時來向用戶呈現內容。當你發現有必要讓你的應用運行在后臺的時候,iOS會高效的幫你做到,并且不會浪費系統資源或者用戶的電量。iOS提供的技術分為三類:
- 在前臺啟動一個簡短任務的應用,能在應用進入后臺的時候請求一點時間來完成這個任務。
- 在前臺啟動下載任務的應用,能把這些下載任務的管理權交給系統,這樣,就能在下載期間允許應用被掛起或者終止。
- 需要運行在后臺來支持特定類型的任務的應用,可以聲明對一個或多個后臺執行模式的支持。
要盡量避免在后臺工作,除非所作的事情有利于改善用戶的綜合體驗。應用進入后臺的原因可能是用戶啟動了另一個應用、或者因為用戶鎖定了設備并且現在它沒有在運行。在這兩種情況下,用戶都表明現在你的應用不需要做任何有意義的事。在這種前提下,繼續運行只會浪費設備的電量,并有可能導致用戶強制退出一的應用。所以要注意你在后臺所作的事,盡可能避免這種情況。
執行有限長度的任務
進入后臺的應用被期望能盡快的進入休眠狀態,一遍它們能被系統掛起。如果你的應用正在任務中,并且需要一點額外的事件來完成任務,它可以調用UIApplication對象的beginBackgroundTaskWithName:expirationHandler: 或beginBackgroundTaskWithExpirationHandler:方法來請求一些額外的執行時間。調用這些方法可以臨時推遲應用的掛起,給完成任務一點額外的時間。完成這項任務后,應用必須調用endBackgroundTask: 方法來讓系統知道任務已完成可以被掛起了。
每次調用beginBackgroundTaskWithName:expirationHandler:還是 beginBackgroundTaskWithExpirationHandler:方法都會產生一個與相應任務關聯的唯一標記(token)。當你的應用完成任務的時候,它必須使用相應的標記調用 endBackgroundTask:方法,以便讓系統知道這個任務完成了。如果為后臺任務調用endBackgroundTask:失敗將會導致應用終止。如果在開始這個任務的時候你提供一個到器處理器(handler),系統會調用這個處理器,并給你最后一個結束任務以及避免應用終止的機會。
你不需要等到應用進入后臺之后再來指定后臺任務。更有用的設計是在開始任務之前調用beginBackgroundTaskWithName:expirationHandler:或beginBackgroundTaskWithExpirationHandler:方法,并且一旦你完成就調用endBackgroundTask:方法。你甚至可以在應用還在前臺執行的時候遵循這個模式。
代碼清單3-1展示了當應用轉換到后臺的時候如何開始一個長時運行任務。在這個例子中,啟動后臺任務的請求包含了一個到期處理器,它的任務就是防治任務耗時過長。然后任務自身被提交給調度隊列進行異步執行,所以applicationDidEnterBackground:方法能夠正常返回。代碼塊(block)的使用精簡了維持對重要變量引用所需的代碼,例如后臺任務的標識符。清單中的bgTask變量是存儲當前后臺任務標識符的類的成員變量,并且被此方法使用之前已初始化。
代碼清單3-1 在退出的時候啟動后臺任務
- (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;
});
}
注意:當啟動任務的時候,總是提供一個到期處理器,但如果你想要知道應用運行了多長時間,請獲取UIApplication的backgroundTimeRemaining屬性的值。
在你自己的到期處理器中,你可以包含必要的額外代碼來關閉你的任務。但是,任何你飽含的代碼都不能花費太長時執行時間,因為,在到期處理器被調用的時候,應用已經非常接近它的限定時間了。因此,僅執行最少的狀態信息清理,并結束任務。
在后臺下載內容
當下載文件時,應用應該使用NSURLSession對象來啟動下載,這樣系統能夠控制下載過程,以防應用被掛起或終止。當你為后臺傳輸配置一個NSURLSession對象時,系統在獨立的進程里管理這些傳輸,并且通過常規方式返回狀態報告給應用。如果應用在傳輸過程中被終止,系統會在后臺繼續這個傳輸,并在傳輸結束或者一個及多個任務需要應用注意的時候啟動應用(以合適的方式)。
為了支持后臺傳輸,你必須恰當地配置你的NSURLSession對象。要配置會話,你必須先創建一個NSURLSessionConfiguration對象,并將幾個屬性設置恰當地值。然后,在你創建會話的時候,把配置好的對象傳遞給適當地NSURLSession初始化方法。
創建支持后臺下載的配置對象的過程如下所示:
- 使用NSURLSessionConfiguration的backgroundSessionConfigurationWithIdentifier:方法來創建配置對象。
- 將配置對象的sessionSendsLaunchEvents屬性設置為YES。
- 如果應用在前臺啟動了傳輸,建議將配置對象的discretionary屬性也設置為YES。
- 恰當地配置其他配置對象的其他屬性。
- 使用這個配置對象創建NSURLSession對象。
一旦配置完成,你的NSURLSession對象會無縫地在適當的時候將上傳和下載的任務交給系統。如果在應用還在運行的時候任務完成(無論是前臺還是后臺),會話對象都會以正常方式通知它的委托對象。如果任務還沒有完成而系統終止了應用,系統會自動的在后臺繼續管理這些任務。如果用戶終止了應用,則系統就會取消任何等待中的任務。
當所有與后臺會話關聯的任務完成的時候,系統重啟終止的應用(假定sessionSendsLaunchEvents屬性被設置為YES,并且用戶沒有強退應用),并且調用應用的委托對象的application:handleEventsForBackgroundURLSession:completionHandler:方法。(系統還可以重啟應用來處理驗證,或者其他需要應用注意的任務相關的事件)。在你的委托方法實現中,使用提供的標識符創建新的與之前配置相同的NSURLSessionConfiguration和NSURLSession對象。系統會將你的心會話對象連接到之前的任務,并將它們的狀態報告給會話對象的委托對象。
實現長時運行任務
為了實現那些請求了更多執行時間的任務,你必須請求特定的許可來讓他們在后臺運行而不被掛起。在iOS中,只有特定的幾種應用類型才被允許在后臺運行:
- 在后臺時向用戶播放音頻內容的應用,例如音樂播放應用。
- 在后臺時錄制音頻內容的應用。
- 隨時記錄用戶位置信息的應用,例如導航應用。
- 支持互聯網語音協議(Voice over Internet Protocol,VoIP)的應用。
- 需要經常下載和處理新內容的應用。
- 從外部配件接收定期更新的應用
實現這些服務的應用必須聲明它們支持這些服務,并且使用系統框架來實現這些服務的相關方面。聲明服務讓系統知道你使用了哪個服務,但在某些情況下,事實上阻止你的應用被掛起的是系統框架。
聲明應用支持的后臺任務
那些后臺執行的類的支持,必須通過使用它們的類應用提前聲明。從Xcode 5開始,從項目設置的Capabilities選項卡中聲明你的應用支持的后臺模式。啟用Background Modes選項將UIBackgroundModes鍵添加到到應用的Info.plist文件。選擇一個或多個復選框來添加相應的后臺模式值到那個鍵。表3-1羅列了你能指定的后臺模式,以及Xcode分配給應用Info.plist 文件中的UIBackgroundModes鍵的值。
表3-1 應用的后臺模式
Xcode后臺模式 | UIBackgroundModes值 | 描述 |
---|---|---|
Audio and AirPlay | audio | 應用向用戶播放聲音內容,或者在后臺錄制音頻。(這個內容包含使用AirPlay的音頻流或視頻流。) 用戶必須在第一次使用前允許應用使用麥克風。更多信息,參見Supporting User Privacy。 |
Location update | location | 應用允許保存用戶的位置信息,甚至當應用運行在后臺的時候。 |
Voice over IP | voip | 應用提供一種讓用戶使用網絡連接打電話的功能。 |
Newsstand downloads | newsstand-content | 應用是Newsstand應用,它在后臺下載并處理雜志或報紙的內容。 |
External accessory communication | external-accessory | 應用與需要通過External Accessory框架提供定期更新的硬件附件配合使用。 |
Uses Bluetooth LE accessories | bluetooth-central | 應用與需要通過Core Bluetooth 框架提供定期更新的藍牙附件配合使用。 |
Acts as a Bluetooth LE accessory | bluetooth-peripheral | 應用通過Core Bluetooth框架支持在外設模式下的藍牙通信。 使用這個模式需要用戶授權;更多信息參見Supporting User Privacy。 |
Background fetch | fetch | 應用定期從網絡下載和處理少量內容。 |
Remote notifications | remote-notification | 應用想要在推送通知到達的時候開始下載內容。使用通知來最小化顯示推送通知相關的內容的延時。 |
以上的每種模式讓系統知道你的應用在合適的時候會被喚醒或者啟動過以響應相關的事件。例如,應用開始播放音樂,然后它進入后臺,但仍然需要執行時間來填充音頻輸出緩沖器。啟用Audio模式,告訴系統框架,它們應該繼續以適當地間隔來進行必要的調用。如果應用沒有選擇這個模式,所有通過這個應用正在播放或錄制的音頻,在應用進入后臺的時候都會停止。
(未完待續......)