衆所周知,iOS操作系統上的App,不僅僅是外形美,同時其也是以流暢著稱,可以說是極端的流暢。在流暢的外表下,是操作系統對於程序的調度、前後臺的切換、內存的管理都達到了極致。
在日常的iOS開發中,不免會遇到需要App在被切到後臺之後,仍然需要運行一個進程完成某些特殊的操作,一般後臺運行進程作用有以下幾類:
- 後臺更新數據、同步刷新UI
- 與服務端通信,發送心跳,如“XMPP”類App
- 獲取用戶地理信息數據(GPS),如新“高德地圖”
- 多媒體App用來繼續播放音樂,如“酷狗音樂”
- 網絡電話類App(VoIP)
要完成上述操作,有以下集中解決方案,每種方案各有優劣,適用於不同情況:
一、VoIP
優勢:
- 永遠不會被後臺進程強殺
- 實現起來簡單
劣勢:
- 只能限定於VoIP類App使用。如無這VoIP功能,如果將App上傳到AppStore,則會被拒絕。(雖然看起來沒有問題,但是蘋果就是這麼“吹毛求疵”)
聽名字就知道,這個方案是Apple專門爲VoIP類App量身定製的。如果你的App並沒有VoIP相關功能,在提交AppStore時會被拒絕,但是如果你只是企業內部分發,並不提交AppStore審覈的話,就沒有啥問題了。使用方法如下:
步驟一、在XCode中爲你的Ap工程打開Background Modles,並勾選Voiceover IP選項,等於告訴iOS操作系統你需要此項功能,系統不能在後臺強制殺掉我的進程,因爲我要在後臺進行一些操作。
同時,也可以在工程的plist文件中加入如下選項:
Key:Required background modes
Value:App provides Voice over IP services
步驟二、在需要後臺運行的地方執行操作
[[UIApplication sharedApplication] setKeepAliveTimeout:600 handler:^{
//幹活
}];
這個函數會以設定的時間間隔被調用,不僅僅在後臺,App在前臺時候,這個函數一樣能夠被調用。所以可以把它當作一個橫跨前後臺的NSTimer來使用。
注意:
1、間隔時長必須大於600s,適合和服務器保持通訊,保證用戶登錄狀態。
2、復活後,執行時間只有10s,之後又繼續掛起。
3、 使用clearKeepAliveTimeout來取消喚醒
4、app必須是能和VoIP功能相關的內容,否則提交App Store會被拒絕。
二、Background fetch方式:
優勢:
- 是官方推薦的後臺數據刷新方法
- 實現比較簡單
劣勢:
- 程序被喚醒執行的時間不可控
- 被喚醒後執行時長有限制
步驟一、設置Background fetch:
步驟二、設置被喚醒最小時間間隔:
[applicationsetMinimumBackgroundFetchInterval:1.0f];
步驟二、需要執行的代碼片段
- (void)application:(UIApplication *)applicationperformFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
注意:
1、代碼被執行的時間間隔不可控,操作系統會以科學的方式來調用。
2、設置的時間只是最小時間間隔、操作系統只能保證在最小時間間隔內不會被調用,但是至於是1m還是1h還是多長時間調用,與操作系統當前的狀態、用戶打開App的頻率有關係。
3、被操作系統喚醒後,只能有30s的時間執行你的任務,30s後,將被再次掛起。按說以國內的網絡環境來說,基本上30s都能解決問題了。
三、播放空白聲音配合“backgroundTask”進行後臺運行
優勢:
- 適用於需要進行後臺播放音樂,記錄地理位置變化等功能的App
- 可以用來進行一些簡單的後臺操作(如保活),而不被AppStore的審覈人員拒絕,比較隱蔽
- 喚醒時間間隔可控
劣勢:
- 實現相對於其他幾種方案都要麻煩
步驟一:在Info.plist中,添加"Required background modes"鍵,value爲:App plays audio,申明你需要使用後臺播放音頻的功能。
步驟二:調節音頻會話的特性:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
// Override point for customization after application launch.
NSError *setCategoryErr = nil;
NSError *activationErr = nil;
[[AVAudioSession sharedInstance]
setCategory: AVAudioSessionCategoryPlayback
error: &setCategoryErr];
[[AVAudioSession sharedInstance]
setActive: YES
error: &activationErr];
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
- (void)applicationDidEnterBackground:(UIApplication *)application{
UIApplication* app = [UIApplication sharedApplication];
__block UIBackgroundTaskIdentifier bgTask;
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
dispatch_async(dispatch_get_main_queue(), ^{
if (bgTask != UIBackgroundTaskInvalid)
{
bgTask = UIBackgroundTaskInvalid;
}
});
}];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_async(dispatch_get_main_queue(), ^{
if (bgTask != UIBackgroundTaskInvalid)
{
bgTask = UIBackgroundTaskInvalid;
}
});
});
}
這樣,就可以在你需要重複執行的地方,實現一個NSTimer,有了上面的步驟之後,你的NSTimer就能不論前臺後臺都可以運行了。
注意:
和前三個不一樣,前三個解決方案是程序被掛起之後又被喚醒,但是,這個解決方案是程序一直在後臺運行,會非常耗內存,需要開發者關注。
四、遠程消息推送,激活
優點:
- 實現簡單
- 喚醒時間可控,由後臺控制
缺點:
- 需要後臺編碼,跑服務進行App喚醒
步驟一、服務端設置推送的消息中加上“content-available”:
{
"aps" : {
"content-available" : 1
},
"content-id" : 42
}
步驟二、在接收消息的地方,進行喚醒後需要的操作