在iOS7以前的系統中,App默認是不能後臺運行的,如果要後臺運行,可以採用以下兩類方法:
(1)使用beginBackgroundTaskWithExpirationHandler函數,向系統申請一段時間來執行需要後臺運行的操作,這種方法的缺點是,後臺操作最多隻能運行10分鐘,超過10分鐘之後App會休眠。使用這種方法需要APPNAME-info.plist中設置Application does not run in background爲NO,然後在適當的時間調用beginBackgroundTaskWithExpirationHandler函數。
(2)將App的後臺運行模式設置爲audio 、VOIP、location、Newstand等。使用這種方法,可以無限制的在後臺運行,以audio爲例,將plist中的Required background modes項目設置爲App plays audio or streams audio/video using AirPlay,並且在進入後臺時播放無聲音樂,就可以讓App一直運行。這種方法的缺點是,如果使用不當,可能會被AppStore拒絕。因爲審覈時是可以通過靜態分析知道使用了哪些API的,如果一個程序本來就不是音樂類的,卻使用了播放音樂的API後臺播音樂,有可能就被拒絕,如果想要繞過這個限制,可以向APP增加播放音樂的功能,但這樣實際是增加了無用功能。
在iOS7以後,系統增加了兩種後臺的模式,一種是Background fetch ,另一種是Remote notification,下面分別介紹。
Background fetch: 設置了這種後臺方式之後,當App休眠之後,會隔一段時間被系統喚醒,從而執行一段短時間操作。喚醒的間隔由系統決定,App中可以設置[[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];,但即使設置了,間隔也不確定是多少。另外,App被喚醒後,可以執行操作的時間也不長,文檔上描述的30秒左右(實際上更長也可以,但是可能會降低以後被喚醒的機率)。
Remote notification:在iOS7以前,當系統收到推送消息後,會立即彈出消息提示用戶,用戶點擊消息之後,就可以啓動App,然後加載數據。使用了這種新的後臺模式之後,當系統收到推送消息之後,會喚醒App,給App一個機會執行一部分操作,等操作之後才提醒用戶,而且還支持silent模式,即執行完操作之後,完全不對用戶做任何提醒,默默的就在後臺把活幹完了。
除了增加了上述的兩種新後臺模式以外,ios7還增加了一下傳輸數據的方法,即Background Transfer service 。
(1)Background Transfer service概述
這種方法的名字很容易讓人誤解,以爲是App進入後臺時,使用這種方法進行數據傳輸。實際上,這種方法與後臺無關。 當App使用了這種方法後,可以將一個下載任務交給系統的獨立進程去下載,不管App在前臺、休眠、以及crash,下載過程都在進行,因爲是系統的獨立進程在爲App進行下載。當系統的下載任務結束或者出錯時,系統會喚醒App,調用其中的函數,讓App做一部分處理,比如讓App重新添加其他任務。這裏有一個缺點就是,如果因爲沒有網絡導致系統下載失敗了,系統即使喚醒了App,App也是沒有辦法下載的,然後App會進入休眠,即使後面有了網絡,系統也不會繼續下載,因爲只要系統向App發出了失敗的信號,除非App 調用resume函數來恢復下載過程,系統是不會自己恢復下載的。這裏就需要用到前面提到的fetch後臺模式,讓App過一段時間被系統喚醒,然後App就可以去檢查網絡,當有網時恢復下載過程。
(2)相關類介紹
NSURLSession session類
NSURLSessionConfiguration 用於初始化session的配置類
NSURLSessionTask—The base class for tasks within a session. 所有task的基類
NSURLSessionDataTask 用來讀取url的返回內容的task類(不支持background session)
NSURLSessionUploadTask用於上傳文件的task類
NSURLSessionDownloadTask 用於將url下載成爲臨時文件的task類
NSURLSessionDelegate 處理session級別的事件
NSURLSessionTaskDelegate處理所有task級別的通用事件
NSURLSessionDataDelegate 處理與讀取Data有關的事件
NSURLSessionDownloadDelegate 處理與下載文件有關的事件
(3)使用步驟(以下載文件爲例)
1. 創建URLSession
- (NSURLSession *)backgroundSession
{
//Use dispatch_once_t to create only one background session. If you want more than one session, do with different identifier
static NSURLSession *session = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration backgroundSessionConfiguration:session_id];
configuration.discretionary = YES;
session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
});
[configDict setObject:session_id forKey:@"session_id"];
return session;
}
2.創建DownloadTask
NSURL *downloadURL = [NSURL URLWithString:@"http:// 17-45990.dmg"];
NSURLRequest *request = [NSURLRequest requestWithURL:downloadURL];
self.session = [self backgroundSession];
self.downloadTask = [self.session downloadTaskWithRequest:request];
[self.downloadTask resume];
當創建完task,並且resume之後,任務就開始下載了
3.實現下載的回調,接收事件
(1) 在需要響應回調的類裏面實現NSURLSessionDelegate、NSURLSessionTaskDelegate、NSURLSessionDownloadDelegate等協議
(2)實現以下函數:
URLSession:downloadTask: didWriteData 獲得當前下載的數據大小及總大小
URLSession: downloadTask: didFinishDownloadingToURL 成功下載之後調用,可以獲得臨時文件的本地地址
URLSession: task: didCompleteWithError 文件下載失敗的回調
URLSessionDidFinishEventsForBackgroundURLSession: 一個session結束之後,會在後臺調用
application: performFetchWithCompletionHandler: 當App被fetch喚醒時調用
application: handleEventsForBackgroundURLSession:completionHandler: 在這個函數中檢查是否傳輸已經完成,然後調用completionHandle來更新AppSwitcher界面
4. 關於斷點續傳
(1)由於下載過程是由系統在處理,即使App被殺死也不影響下載,因此App無需在斷網或者退出時記錄當前的下載位置。
(2)當下載過程開啓後,只要系統沒有發出失敗信號,即使斷網了、系統關機了,等恢復網絡或者系統重啓之後,系統會繼續下載,此時app只需要創建與上次相同id的session,即可接收到下載進度信息。如果app收到了失敗信號,需要從nserro中通過userinfo來獲取resumedata,從而在下次恢復下載時,使用downloadTaskWithResumeData函數來創建task,這樣就可以斷點續傳,而不是用downloadTaskWithRequest來創建task,後者會開啓一個新的下載。