前言
對于APP的前后臺運行情況的了解,有助于我們在實際開發中規避一些問題,以及采取穩妥的方法處理和解決問題,是很必須的。
應用的運行狀態分為以下五種:
- Not running:應用還沒有啟動,或者應用正在運行但是途中被系統停止。
- Inactive:當前應用正在前臺運行,但是并不接收事件(當前或許正在執行其它代碼)。一般每當應用要從一個狀態切換到另一個不同的狀態時,中途過渡會短暫停留在此狀態。唯一在此狀態停留時間比較長的情況是:當用戶鎖屏時,或者系統提示用戶去響應某些(諸如電話來電、有未讀短信等)事件的時候。
- Active:當前應用正在前臺運行,并且接收事件。這是應用正在前臺運行時所處的正常狀態
- Background:應用處在后臺,并且還在執行代碼。一般的應用,都只會在這個狀態短暫停留(最多十分鐘),然后就會被系統強制進入 Suspended 狀態。而 iOS 為了在某些情況下提供更好的體驗,提供了一些選項,只要滿足這些選項的條件,就可以在后臺運行很長的一段時間,下面我們將重點討論可以使應用在后臺長時間運行的方法。
- Suspended:應用處在后臺,并且已停止執行代碼。系統自動的將應用移入此狀態,且在此舉之前不會對應用做任何通知。當處在此狀態時,應用依然駐留內存但不執行任何程序代碼。當系統發生低內存告警時,系統將會將處于 Suspended 狀態的應用清除出內存以為正在前臺運行的應用提供足夠的內存。
想了解更多,推薦一篇很好的文章:iOS應用程序生命周期(前后臺切換,應用的各種狀態)詳解
后臺模式
有時候我們想讓APP在后臺運行,可是蘋果對后臺模式一直審核很嚴格,在我看來,蘋果限制 app在后臺運行,是為了更有效的利用硬件使用當前的app,不然,過多的app駐留后臺,對手機資源占用是一大問題。,那么后臺模式是什么呢,以及怎樣實現呢?
iOS 提供的后臺運行方式
上圖為 iOS 提供的后臺運行方式列表,如果需要,可在 Xcode 的項目設置中開啟對應的選項。App Store 的審核人員會檢查應用中是否有必要開啟該后臺運行模式選項,如果應用中不需要,而又開啟了這個選項,可能會被拒,并且這部分的審核是很嚴格的,如果不能提供證據證據,是肯定會被拒。
-
Audio, AirPlay and Picture in Picture
此個選項包含四種場景,分別是:音頻的播放,錄音,AirPlay 及畫中畫的視頻播放。
音頻的播放:在播放音頻時,即使應用退到后臺,只要一直有音頻在播放,那應用就可以一直在后臺運行。代碼實現可參考:http://www.linuxidc.com/Linux/2012-08/68364.htm
錄音:應用可以請求使用麥克風,而當開啟了此后臺選項,應用在使用麥克風的時候,即使退到后臺,也可以一直后臺運行,通過查看微信安裝包中的 plist 文件,微信的語音聊天,就是通過這種方式實現的。而當該類應用退到后臺后,iOS 系統的狀態欄會變成紅色,并在狀態欄中顯示正在使用麥克風的應用的名稱,如下圖所示。
AirPlay:AirPlay 是指將 iOS 設備,或者 Mac 設備上的音視頻,同步到另一個設備中播放。舉兩個例子,第一個是把 iPhone 上的音樂通過藍牙的方式在汽車的藍牙音響播放,第二個是把 iPhone 上的視頻,同步到智能電視屏幕上播放。此功能一般用于多端及多屏的交互。關于 AirPlay 的開發文檔:http://nto.github.io/AirPlay.html
畫中畫的視頻播放:畫中畫是 iPad 版本的 iOS 9 新增加的功能,可以在 iOS 的桌面,或者其他應用的界面的上面播放視頻,從而該視頻區域所屬的應用就可以后臺運行了。此功能現在只在 iPad 應用中提供。代碼實現可參考:http://www.cocoachina.com/ios/20150714/12558.html
-
Location updates
一般用于導航應用中,開啟此選項后,應用退到后臺,還可以得到系統的定位更新,從而使得應用可以根據定位的變化做出不同的反應。代碼實現可參考:https://github.com/voyage11/Location
-
Voice over IP
VOIP 類的應用允許用戶使用網絡而不是手機打電話,因此這一類的應用需要保持同它相關的服務的網絡連接,用以收到來電事件和其他數據。iOS 不是通過一直讓該應用處于激活狀態來達到這個目的,而是同樣也會將這類的應用掛起,但同時會在應用被掛起期間由系統接管它的 VOIP 的 Socket,當這個 Socket 有數據通信時,系統會再次喚醒處于掛起狀態的應用,同時將 Socket 的控制權交還給該應用,以讓其正常的處理來電事件和其他數據。
其中VOIP需要綁定一個Socket鏈接并申明給系統,系統將會在后臺接管這個連接,
#一旦遠端數據過來,你的App將會被喚醒10s(或者更少)的時間來處理數據,超過時間或者處理完畢,程序繼續休眠
-
Newsstand downloads
在 iOS 開發中,有一類叫報刊雜志類應用比較特別,在 iOS 9 之前的系統中,此類應用會統一收在系統內置的「報刊雜志」應用中,在 iOS 9 中則去掉了內置的「報刊雜志」應用,此類應用得以以單獨的圖標入口出現在桌面中。此后臺運行的選項就是提供給報刊雜志類應用可以在后臺下載及處理報刊雜志內容,而下載的過程需要使用 NewsstandKit 中的 NKAssetDownload 進行下載。需要注意的是,下載的過程中,應用可能還是會被掛起,甚至應用被退出,而 iOS 會在 Wi-Fi 環境下繼續下載,直到下載完成。而一旦下載完成,如果應用只是被掛起,則** iOS 會喚醒對應的應用,回調對應的事件;如果應用已經退出,則會啟動應用**,在啟動參數中會帶上對應的標識表示這次啟動是因為下載報刊雜志內容完成。代碼實現可參考:http://www.viggiosoft.com/blog/blog/2011/10/17/ios-newsstand-tutorial/
-
External Accessory communication
此選項提供給一些 MFi 外設通過藍牙,或者 Lightning 接頭等方式與 iOS 設備連接,從而可在外設發送消息時,喚醒已經被掛起的應用。而一旦被喚醒,一般情況下, 應用只有最多 10 秒鐘的執行時間。MFi 外設:是指通過蘋果 MFi 認證的設備,而 MFi 認證是對其授權配件廠商生產的外置配件的一種標識使用許可,是 Made for iOS 的英文縮寫。
-
Uses Bluetooth LE accessories
此選項與 External Accessory communication 類似,只是此選項無需限制 MFi 外設,而需要的是 Bluetooth LE 設備。
-
Acts as a Bluetooth LE accessory
此選項是指 iOS 設備作為一個藍牙外設連接時,對應的應用可以后臺運行,但是使用此模式需要用戶進行授權認證。
-
Background fetch
iOS 7 新增加的一個選項,用于即使在后臺,也需要頻繁更新數據的應用。例如一個 PM2.5 的應用,需要幾個小時更新一次數據,那么可以開啟此選項,設置一個時間間隔,從而讓 iOS 在間隔時間內在后臺啟動該應用,執行指定數據的獲取工作,而此過程最多只能執行 30 秒鐘。代碼實現可參考:http://objccn.io/issue-5-5/
-
Remote notifications
iOS 7 新增加的一個選項,是一種靜默推送,它有別于一般的推送,應用收到此類推送后,不會有任何的界面提示,而當應用退出或者掛起時收到此類推送,iOS 也會啟動或者喚醒對應的應用。例如一個閱讀應用,用戶訂閱的博客更新了,那么可以先發一個靜默推送,應用收到此種推送后,可以先把用戶訂閱的博客內容都下載好,再通知用戶,這樣用戶一打開應用就可以馬上開始閱讀。收到靜默推送,會回調對應的回調方法,而此回調方法最多只能執行 30 秒鐘。代碼實現可參考:http://objccn.io/issue-5-5/
#需要注意的是:
iOS 7 以前,應用進入后臺繼續運行時,如果用戶鎖屏了,那么 iOS 會等待應用運行完,才進入睡眠狀態。
而在 iOS 7 上,系統會很快進入睡眠狀態,那些后臺應用也就暫停了。
#如果收到事件被喚醒(例如定時事件、推送、位置更新等),后臺應用才能繼續運行一會。
因為處理過程變成了斷斷續續的,因此下載時也要使用 NSURLSession 來處理(即下文中的 Background Transfer Service)。
-
基于 NSURLSession 的后臺傳輸
此為 iOS 7 新增加的特性,用于在后臺下載或者上傳大文件,步驟如下:創建后臺傳輸用的 NSURLSession 對象;向這個對象中加入對應的傳輸的 NSURLSessionTask,并開始傳輸;在實現 AppDelegate 里實現 -application:handleEventsForBackgroundURLSession:completionHandler: 方法,以刷新 UI 及通知系統傳輸結束。一旦后臺傳輸的狀態發生變化(包括正常結束和失敗)的時候,應用將被喚醒并運行 AppDelegate 中的回調。但是也有一些限制,后臺傳輸只會通過 Wi-Fi 來進行。后臺下載的時間與以前的關閉應用后X分鐘的模式不一樣,而是為了節省電力變為離散式的下載。代碼實現可以參考:http://onevcat.com/2013/08/ios7-background-multitask/
保持程序在后臺長時間運行
iOS為了讓設備盡量省電,減少不必要的開銷,保持系統流暢,因而對后臺機制采用墓碑式的“假后臺”。除了系統官方極少數程序可以真后臺,一般開發者開發出來的應用程序后臺受到以下限制:
用戶按Home之后,App轉入后臺進行運行,此時擁有180s后臺時間(iOS7)或者600s(iOS6)運行時間可以處理后臺操作
當180S或者600S時間過去之后,可以告知系統未完成任務,需要申請繼續完成,系統批準申請之后,可以繼續運行,但總時間不會超過10分鐘。
-
當10分鐘時間到之后,無論怎么向系統申請繼續后臺,系統會強制掛起App,掛起所有后臺操作、線程,直到用戶再次點擊App之后才會繼續運行。
#申請后臺處理時間的方法: //申請后臺,該方法只有在App處于激活 beginBackgroundTaskWithExpirationHandler:時調用才有效。 //注銷后臺 endBackgroundTask: #import "AppDelegate.h" @interface AppDelegate () @property(assign, nonatomic)UIBackgroundTaskIdentifier backIden; @end @implementation AppDelegate - (void)applicationDidEnterBackground:(UIApplication *)application { [self beginTask]; } - (void)applicationWillEnterForeground:(UIApplication *)application { NSLog(@"進入前臺"); [self endBack]; } //申請后臺 -(void)beginTask { NSLog(@"begin============="); _backIden = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ #在時間到之前會進入這個block,一般是iOS7及以上是3分鐘。 #按照規范,在這里要手動結束后臺,你不寫也是會結束的,但是不寫有可能會crash。 NSLog(@"將要掛起============="); [self endBack]; }]; } //注銷后臺 -(void)endBack { NSLog(@"end============="); [[UIApplication sharedApplication] endBackgroundTask:_backIden]; _backIden = UIBackgroundTaskInvalid; } @end
使用后臺模式可以使APP在后臺持續運行,不過下面的這倆方法是很多想實現后臺長時間運行的APP都可以嘗試的。
- 有的開發者為了自己的APP能在后臺運行想出一直循環播放一段沒聲音的音頻,在后臺選項中選擇「Audio, AirPlay and Picture in Picture」,而開始循環播放一段是沒聲音的音頻,即在 Audio Unit 回調函數中使用 kAudioUnitRenderAction_OutputIsSilence 標志位,但是這種方式蘋果的審核人員如果發現,會被拒,基本上都會被發現。
- 使用定位服務的方法來保持后臺,在程序轉入后臺的時候,啟動定位服務[locationManager startUpdatingLocation];(第一次運行這個方法的時候,如果之前用戶沒有使用過App,則會彈出是否允許位置服務)。這樣在定位服務可用的時候,程序會不斷刷新后臺時間,實際測試,發現后臺180s時間不斷被刷新,達到長久后臺的目的。
小結
關于應用后臺模式運行以及其它相關的知識,后續會持續更新
本文參考文章
iOS開發:后臺運行
iOS 后臺運行實現