一、iOS的“偽后台”程序
首先,先了解一下iOS 中所謂的「后台進程」到底是怎么回事吧?
Let me be as clear as I can be: the iOS multitasking bar does not contain "a list of all running apps". It contains "a list of recently used apps". The user never has to manage background tasks on iOS.
首先我們得搞清楚的是,iOS 中所謂的「后台駐留」並不是指「執行中的程序」,而是「最近使用過的程序」,也可以瞧瞧Apple 的知識庫文章怎么寫的:
按兩下主畫面按鈕后,顯示的是「最近用過的應用程序」,所以別把它想成Mac OS X 中的Command + Tab 鍵所顯示的切換程序列;你可以試試看將iPhone 關機再開機,然后按兩下Home 鍵,應該會發現先前那些程式還是出現在那里,因為它其實是你最近使用過的程序使用記錄,或像是瀏覽器中的瀏覽記錄)。
如果你一個一個去清除 "后台駐留" 的程序,就像是一筆一筆刪除瀏覽記錄中的項目一樣,是沒有太大意義的(除非你不想讓其他人看到你曾經用過什么程式,或者想殺時間、太無聊了),也不會對電力或記憶體有什么幫助。
iOS 的多任務處理其實是很聰明的,系統會自動中止在背景執行的程序,在有需要時,也會將被中止的程式suspended(注:"中止的程式"不是指關閉程序,而是指由前台轉向后台的程序,程序在后台並不真正運行,不要和關閉程序混淆了) 從內存中移除。因此,iOS 裝置的使用者根本就不需要擔心怎么管理這些執行中或不在執行中的程序,盡管放心用、輕松玩就是了!
一般說來,所有程式進入背景后都還有5 秒的執行緩沖時間,有些程序可以要求延長到10 分鍾(這些當然是由開發人員在設計與提交程式時決定和聲明);
因此,在你按下Home 鍵后,本來的程式就會退到背景,如果它有額外的背景執行作業,超過10 分鍾還是會被iOS 中止;所以,還是別擔心了!
但是,也有例外情況,某些特殊類型的程式是被容許在背景中持續執行的,像是會持續在背景播放音樂的程序、GPS 程序、VOIP 程序、Newsstand 以及周邊配件附屬的程序,不過只要這些程序不再執行動作,就會變成中止的程式,像是音樂撥完了、雜志下載完了... 因此,使用者其實也沒必要太過留意這些程序。
you do not have to manage background tasks on iOS. The system handles almost every case for you and well written audio, GPS, VOIP, Newsstand and accessory apps will handle the rest.
二、iOS的“多任務”發展史
自從出現了操作系統以來,就沒有過所謂真正的“多任務”。所謂多任務,無非是CPU速度夠快而足以支撐極短時間內在多個進程內動作罷了。而所謂的“單任務”,無非是只能讓當前任務獨享資源罷了。
簡單來說,iOS 4 APP類型可以分為三種:
1. 保存現場。
即:按下Home鍵10秒內直接殺死進程,並釋放內存。一般說來,所有程序進入背景后都還有5 秒的執行緩沖時間,有些程序可以要求延長到10 分鍾(這些當然是由開發人員在設計與提交程式時決定和聲明);因此,在你按下Home 鍵后,本來的程式就會退到背景,如果它有額外的背景執行作業,超過10 分鍾還是會被iOS 中止。
2. iOS支持的“多任務”。
即:按下Home鍵轉入多任務狀態,保留在內存中,但只能系統允許的動作:比如GPS,比如VoIP,比如Streaming Music等等。
3. 真正的桌面級別的多任務。
即:只有Safari/Mail才是真正的多任務,蘋果嫡系大都都不是。這個級別的app在后台是沒有任何限制動作。(對於無限制動作的程序,一是會在用戶無察覺的情況下耗光電力,二是會有安全上面的問題)。
注意:對於上述三種級別,后兩種會占用內存的APP,也會在任意時間從內存中被砍掉,取決於你是否動用了其它app而導致內存不足。真正不會被砍掉的后台,只有蘋果那個通知系統。
其實可以回顧一下iOS是怎么一步步引入現在這個機制的:
1. 剛出生,什么都沒有,單純的單任務。
2. iPhone OS 3.0,引入通知架構。幾乎當年全部機型可用;Android晚了一年,2.2才有此功能,到這個時候相信才有90%上下機器用上。
3. iOS 4.0+,引入有限制的后台多任務。
搞這么復雜干嘛?直接像Android一出生那樣所有app一個小虛擬機,無任何限制動作,讓根本沒有內存管理經驗和資源消耗概念的全世界最大民工群Java程序員們寫的app任意地跑着吧!
答案只有兩個:電力,安全。安全不說,如何在保持多任務優勢的同時,避免消耗過多電力,蘋果花了很多心思;而不是像webOS/Android一樣,簡單粗暴桌面級的多任務。iPhone 4甚至在硬件上面也下了苦功,利用3D層疊技術組裝A4芯片,內部元件高度集成,並把天線移至體表,增加內部電池空間。
其實至iPhone OS 3,已經足夠應付大部分應用了。用戶根本無從知道app的實際運行狀態,保存現場讓app看起來不像是剛打開一樣,通知系統又可以在后台默默推送消息。事實上,當前95% iOS APP依舊只有“保存現場 + 通知系統”這樣的組合,比如很多twitter客戶端,大部分人根本不會注意到它不是真正意義上的“多任務”。很震撼的事實,但確實95%的應用場景根本不需要所謂真正的“多任務”。
但是時代在變。真正需要多任務的東西出現了:電台要stream,IM要stream,GPS要stream,多任務必須提上日程。iOS 4加進了,WP7剛剛加進了,Android/webOS打一出生就有。從本質來說,iOS/WP7/Android/webOS跑在內存中的后台程序,唯一的區別就是,iOS/WP7限制了它可以做的事(這就是為什么它們一出生都沒有多任務的原因,這個架構很復雜,不是幾百行中文字可以說明白的),而Android/webOS沒有;從技術上面來說,iOS/WP7/webOS三者架構相當,Android獨立門戶。
於是很可笑,有無限制,成了真“多任務”和偽“多任務”的區別;而后者架構更復雜,可以套用一句“吃力不討好”。
為什么要做吃力不討好的事?答案依舊在上面,電力和安全。當然你可以說iPhone電力也沒好到哪里去,安全也就一般般嘛。暫且拋掉這兩樣不談,實際看看iOS有限制后台app,和Android完全無限制后台app有什么不同:
相信我,最終你會發現iOS上最令人詬病的app,是那些不支持標准協議的軟件平台商們,比如騰訊。因為iOS后台允許的動作,已經覆蓋了全部大大小小的通信開源協議。
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
三、IOS長時間后台運行的實現代碼
1、檢查設備是否支持多任務
Apple出於性能的考慮,並不是所有的iOS設備升級到iOS4以后都支持多任務,比如iPhone 3G。如果你的應用在沒有多任務特性時會出問題,為了保持應用的健壯性,你應該對此進行判斷並處理。你可以通過調用UIDevice對象的multitaskingSupported屬性來獲取當前設備是否支持多任務。
- if(![UIDevicecurrentDevice].multitaskingSupported)
- {
- //不支持多任務時應做的處理
- }
通常,當用戶按一下Home鍵,當前應用就會被進入后台,應用處在后台運行狀態一小段時間后,就會進入掛起(suspend)狀態,此時應用不會再執行任何代碼。如果系統在運行其他應用時內存資源不足,這個掛起的應用甚至有可能被系統退出,釋放內存以供活動的應用使用。只有當用戶再次運行此應用,應用才會從掛起狀態喚醒,代碼得以繼續執行。這就是iOS4帶來的基本的多任務特性,這個特性是一般應用默認支持的,就是說你的應用不需要任何修改就能支持基本多任務特性。2、基本多任務特性
既然是多任務你應該會在應用進入后台時做一些處理,比如暫停一些界面的定時刷新或網絡請求。同時,或者你會在程序進入前台時執行一些恢復操作。在你的應用的application delegate里有2個消息用於處理這些消息:
- - (void)applicationDidEnterBackground:(UIApplication*)application
- {
- //進入后台時要進行的處理
- }
- - (void)applicationWillEnterForeground:(UIApplication*)application
- {
- //進入前台時要進行的處理
- }
也許你需要更多的多任務特性,比如后台播放音樂或者是后台進行GPS跟蹤。這會是下面介紹的內容。當然你也許不會都在application delegate處理所有的事情。如果你要在其他對象中處理,那么你就需要注冊系統通知了,這兩個通知分別是UIApplicationDidEnterBackgroundNotification和UIApplicationWillEnterForegroundNotification。
3、聲明你需要的后台任務
在Info.plist中添加UIBackgroundModes鍵值,它包含一個或多個string的值,包括:
audio:在后台提供聲音播放功能,包括音頻流和播放視頻時的聲音
location:在后台可以保持用戶的位置信息
voip:在后台使用VOIP功能
前面的每個value讓系統知道你的應用程序應該在適當的時候被喚醒。例如,一個應用程序,開始播放音樂,然后移動到后台仍然需要執行時間,以填補音頻輸出緩沖區。添加audio鍵用來告訴系統框架,需要繼續播放音頻,並且可以在合適的時間間隔下回調應用程序;如果應用程序不包括此項,任何音頻播放在移到后台后將停止運行。
除了添加鍵值的方法,IOS還提供了兩種途徑使應用程序在后台工作:
Task completion—應用程序可以向系統申請額外的時間去完成給定的任務
Local notifications—應用程序可以預先安排時間執行local notifications 傳遞
4、后台播放音樂
通常,一般應用在進入后台時,任何聲音就將會停止。這也許不是我們所想要的。要想讓自己的應用支持后台播放,首先要修改應用的Info.plist文件,你需要在Info.plist文件中添加UIBackgroundModes字段,該字段的值是應用支持的所有后台模式,是一個數值類型。目前此數組可以包含“audio”、“location”和“voip”這三個字符串常量,如果要支持后台音樂播放,你就需要包含“audio”,其余兩個會將在后面講到。
同時,你也應該設置一下應用程序的Audio Sesstion。這個是必需的,如果不設置Audio Sesstion,應用就可能進入后台時Audio Sesstion失活而停止播放。一般需要這么設置就可以了:
- [[AVAudioSessionsharedInstance]setCategory:AVAudioSessionCategoryPlaybackerror:nil];
5、后台GPS跟蹤
和后台播放音樂類似,若要支持后台GPS跟蹤,你就需要在Info.plist文件中UIBackgroundModes字段對應的數組中增加“location”字符串。
6、后台voip支持
由於voip應用需要一個長連接到服務器,為了讓這類應用能正常工作,iOS中加入后台voip支持特性。為支持這一特性,需要在Info.plist文件中UIBackgroundModes字段對應的數組中增加“voip”字符串。
此外你仍然需要配置一下你的網絡連接,以便支持后台連接。iOS提供的網絡連接庫有幾種,下面一一說明:
a、如果你使用的是NSStream,如NSInputStream或NSOutputStream,需要調用setProperty:forKey:將Key為NSStreamNetworkServiceType的value設置為
NSStreamNetworkServiceTypeVoIP;
b、如果你使用NSURLRequest,需要調用setNetworkServiceType:將網絡類型設置為NSURLNetworkServiceTypeVoIP;
c、如果你使用CFStream,如CFReadStreamRef或CFWriteStreamRef,需要調用CFReadStreamSetProperty或CFWriteStreamSetProperty將
kCFStreamNetworkServiceType屬性設置為kCFStreamNetworkServiceTypeVoIP。