iOS怎么進行后臺下載,斷點下載
從iOS7以來,蘋果阿爸推出NSURLSession后,iOS現在可以實現真正的后臺下載。
一個NSURLSession對象可以協調一個或多個NSURLSessionTask對象,并且根據NSURLSessionTask創建的NSURLSessionConfiguration實現不同的功能,使用相同的配置,你也可以創建多組具有相關任務的NSURLSession對象,要利用后臺傳輸服務,你將會使用[NSURLSessionConfiguration backgroundSessionConfiguration]來創建一個會話配置,添加到后臺會話的任務在外部進程運行,即使應用程序被掛起,崩潰,或者被殺死,它依然會運行。
下面我們來看看如何使用NSURLSession
下載用到的委托方法
1:AppDelegate委托方法
//在應用處于后臺,且后臺任務下載完成時回調 - (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler; ?????????????????
2:NSURLSession委托方法
/* 在任務下載完成、下載失敗 * 或者是應用被殺掉后,重新啟動應用并創建相關identifier的Session時調用 */ - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error; ?
/* 應用在后臺,而且后臺所有下載任務完成后,
* 在所有其他NSURLSession和NSURLSessionDownloadTask委托方法執行完后回調,
* 可以在該方法中做下載數據管理和UI刷新 ?*/-(void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession*)session;
注:最好將handleEventsForBackgroundURLSession中completionHandler保存,在該方法中待所有載數據管理和UI刷新做完后,再調用completionHandler()
NSURLSessionDownloadTask委托方法
/* 下載過程中調用,用于跟蹤下載進度
* bytesWritten為單次下載大小
* totalBytesWritten為當當前一共下載大小
* totalBytesExpectedToWrite為文件大小
*/-(void)URLSession:(NSURLSession*)sessiondownloadTask:(NSURLSessionDownloadTask*)downloadTaskdidWriteData:(int64_t)bytesWrittentotalBytesWritten:(int64_t)totalBytesWrittentotalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite;
/* 下載恢復時調用
* 在使用downloadTaskWithResumeData:方法獲取到對應NSURLSessionDownloadTask,
* 并該task調用resume的時候調用
? */-(void)URLSession:(NSURLSession*)sessiondownloadTask:(NSURLSessionDownloadTask*)downloadTaskdidResumeAtOffset:(int64_t)fileOffsetexpectedTotalBytes:(int64_t)expectedTotalBytes;
//下載完成時調用- (void)URLSession:(NSURLSession*)session? ? ? downloadTask:(NSURLSessionDownloadTask*)downloadTaskdidFinishDownloadingToURL:(NSURL*)location;
注:在URLSession:downloadTask:didFinishDownloadingToURL方法中,location只是一個磁盤上該文件的臨時 URL,只是一個臨時文件,需要自己使用NSFileManager將文件寫到應用的目錄下(一般來說這種可以重復獲得的內容應該放到cache目錄下),因為當你從這個委托方法返回時,該文件將從臨時存儲中刪除。
創建后臺下載的操作步驟
后臺傳輸的的實現也十分簡單,簡單說分為三個步驟:
1:創建后臺下載用的NSURLSession對象,設置為后臺下載類型;
2:向這個對象中加入對應的傳輸的NSURLSessionTask,并開始下載;
3:在AppDelegate里實現handleEventsForBackgroundURLSession,以刷新UI及通知系統傳輸結束。
4:實現NSURLSessionDownloadDelegate中必要的代理
具體代碼實現
1:創建一個后臺下載對象用dispatch_once創建一個用于后臺下載對象,目的是為了保證identifier的唯一,文檔不建議對于相同的標識符 (identifier) 創建多個會話對象。這里創建并配置了NSURLSession,將通過backgroundSessionConfiguration其指定為后臺session并設定delegate。
- (NSURLSession*)backgroundURLSession {staticNSURLSession*session =nil;staticdispatch_once_tonceToken;dispatch_once(&onceToken, ^{NSString*identifier =@"com.yourcompany.appId.BackgroundSession";NSURLSessionConfiguration* sessionConfig = [NSURLSessionConfigurationbackgroundSessionConfigurationWithIdentifier:identifier]; session = [NSURLSessionsessionWithConfiguration:sessionConfig delegate:selfdelegateQueue:[NSOperationQueuemainQueue]]; });returnsession;}
2:向其中加入對應的傳輸用的NSURLSessionTask,并調用resume啟動下載。
- (void)beginDownloadWithUrl:(NSString*)downloadURLString {NSURL*downloadURL = [NSURLURLWithString:downloadURLString];NSURLRequest*request = [NSURLRequestrequestWithURL:downloadURL];NSURLSession*session = [selfbackgroundURLSession];NSURLSessionDownloadTask*downloadTask = [session downloadTaskWithRequest:request]; [downloadTask resume];}
3.在appDelegate中實現handleEventsForBackgroundURLSession,要注意的是,需要在handleEventsForBackgroundURLSession中必須重新建立一個后臺 session 的參照(可以用之前dispatch_once創建的對象),否則NSURLSessionDownloadDelegate和NSURLSessionDelegate方法會因為沒有 對 session 的 delegate 設置而不會被調用。然后保存completionHandler()。
- (void)application:(UIApplication*)applicationhandleEventsForBackgroundURLSession:(NSString*)identifier completionHandler:(void(^)())completionHandler {NSURLSession*backgroundSession = [selfbackgroundURLSession];NSLog(@"Rejoining session with identifier %@ %@", identifier, backgroundSession);// 保存 completion handler 以在處理 session 事件后更新 UI[selfaddCompletionHandler:completionHandler forSession:identifier]; }
- (void)addCompletionHandler:(CompletionHandlerType)handler forSession:(NSString*)identifier {if([self.completionHandlerDictionary objectForKey:identifier]) {NSLog(@"Error: Got multiple handlers for a single session identifier. This should not happen.\n"); } [self.completionHandlerDictionary setObject:handler forKey:identifier];}
注:handleEventsForBackgroundURLSession方法是在后臺下載的所有任務完成后才會調用。如果當后臺傳輸完成時,如果應用程序已經被殺掉,iOS將會在后臺啟動該應用程序,下載相關的委托方法會在application:didFinishLaunchingWithOptions:方法被調用之后被調用。
4:實現URLSessionDidFinishEventsForBackgroundURLSession,待所有數據處理完成,UI刷新之后在改方法中在調用之前保存的completionHandler()。
//NSURLSessionDelegate委托方法,會在NSURLSessionDownloadDelegate委托方法后執行- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession*)session {NSLog(@"Background URL session %@ finished events.\n", session);if(session.configuration.identifier) {// 調用在 -application:handleEventsForBackgroundURLSession: 中保存的 handler[selfcallCompletionHandlerForSession:session.configuration.identifier]; }} - (void)callCompletionHandlerForSession:(NSString*)identifier { CompletionHandlerType handler = [self.completionHandlerDictionary objectForKey: identifier];if(handler) { [self.completionHandlerDictionary removeObjectForKey: identifier];NSLog(@"Calling completion handler for session %@", identifier); handler(); }}
關于斷點下載可能還會問到的問題
如何暫停下載,暫停后,如何繼續下載?有兩種方法
@第一種,使用cancelByProducingResumeData
/* 對某一個NSURLSessionDownloadTask取消下載,取消后會回調給我們 resumeData,
? ? * resumeData包含了下載任務的一些狀態,之后可以用戶恢復下載
? */- (void)cancelByProducingResumeData:(void(^)(NSData* resumeData))completionHandler;
調用該方法會觸發以下方法,會附帶resumeData,用于恢復。
- (void)URLSession:(NSURLSession*)session task:(NSURLSessionTask*)taskdidCompleteWithError:(NSError*)error
對應恢復方法
//通過之前保存的resumeData,獲取斷點的NSURLSessionTask,調用resume恢復下載NSURLSessionDownloadTask*task = [[selfbackgroundURLSession] downloadTaskWithResumeData:resumeData];[task resume];
第二種,使用NSURLSessionDownloadTask的suspend方法
//暫停[self.downloadTask suspend];
//恢復[self.downloadTask resume];
通過以上的兩個方法,就可以實現下載的暫停與恢復下載了
下載失敗后,如何恢復下載?--下載失敗后,可以通過以下代碼來恢復下載
/* 該方法下載成功和失敗都會回調,只是失敗的是error是有值的,
? * 在下載失敗時,error的userinfo屬性可以通過NSURLSessionDownloadTaskResumeData
? * 這個key來取到resumeData(和上面的resumeData是一樣的),再通過resumeData恢復下載
? */- (void)URLSession:(NSURLSession*)sessiona? ? ? ? ? ? ? task:(NSURLSessionTask*)taskdidCompleteWithError:(NSError*)error {if(error) {// check if resume data are availableif([error.userInfo objectForKey:NSURLSessionDownloadTaskResumeData]) {NSData*resumeData = [error.userInfo objectForKey:NSURLSessionDownloadTaskResumeData];NSURLSessionTask*task = [[selfbackgroundURLSession] downloadTaskWithResumeData:resumeData];? ? ? ? ? ? [task resume];? ? ? ? }? ? }}
應用被用戶殺掉后,如何恢復之前的下載?
在應用被殺掉前,iOS系統保存應用下載sesson的信息,在重新啟動應用,并且創建和之前相同identifier的session時(蘋果通過identifier找到對應的session數據),iOS系統會對之前下載中的任務進行依次回調URLSession:task:didCompleteWithError:方法,之后可以使用上面提到的下載失敗時的處理方法進行恢復下載