標籤:
三種方式使得iOS程式即使在關閉或崩潰的情況下也能夠在後台持續進行一些任務,比如更新程式介面快照,下載檔案等。這三個方法分別是 Background Fetch,Remote Notification和NSURLSession的backgroundSessionConfiguration
Background Fetch開啟
首先在info plist檔案中開啟UIBackgroundModes的Background fetch。或者手動編輯這個值
<key>UIBackgroundModes</key><array> <string>fetch</string></array>
iOS預設不進行background fetch,需要設定一個時間的間隔
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ //UIApplicationBackgroundFetchIntervalMinimum表示儘可能頻繁去擷取,如果需要指定至少多少時間更新一次就需要給定一個時間值 [application setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum]; return YES;}
最後在App Delegate裡實現下面的方法,這個方法只能在30秒內完成。
- (void) application:(UIApplication *)applicationperformFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{ NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration]; NSURL *url = [[NSURL alloc] initWithString:@"http://yourserver.com/data.json"]; NSURLSessionDataTask *task = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { if (error) { completionHandler(UIBackgroundFetchResultFailed); return; } // 解析響應/資料以決定新內容是否可用 BOOL hasNewData = ... if (hasNewData) { completionHandler(UIBackgroundFetchResultNewData); } else { completionHandler(UIBackgroundFetchResultNoData); } }]; // 開始任務 [task resume];}測試
- 通過查看UIApplication的applicationState
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ NSLog(@"Launched in background %d", UIApplicationStateBackground == application.applicationState); return YES;}Remote Notification
在普通的遠程通知裡帶上content-available標誌就可以在通知使用者同時在後台進行更新。通知結構如下
{ "aps" : { "content-available" : 1 }, "content-id" : 42}
接收一條帶有content-available的通知會調用下面的方法
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{ NSLog(@"Remote Notification userInfo is %@", userInfo); NSNumber *contentID = userInfo[@"content-id"]; // 根據 content ID 進行操作 completionHandler(UIBackgroundFetchResultNewData);}利用NSURLSession進行background transfer task
使用[NSURLSessionConfiguration backgroundSessionConfiguration]建立一個背景工作,當應用退出後,崩潰或進程被關掉都還是會運行。
範例,先處理一條遠程通知,並將NSURLSessionDownloadTask添加到後台傳輸服務隊列。
- (NSURLSession *)backgroundURLSession{ static NSURLSession *session = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSString *identifier = @"io.objc.backgroundTransferExample"; NSURLSessionConfiguration* sessionConfig = [NSURLSessionConfiguration backgroundSessionConfiguration:identifier]; session = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:[NSOperationQueue mainQueue]]; }); return session;}- (void) application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{ NSLog(@"Received remote notification with userInfo %@", userInfo); NSNumber *contentID = userInfo[@"content-id"]; NSString *downloadURLString = [NSString stringWithFormat:@"http://yourserver.com/downloads/%d.mp3", [contentID intValue]]; NSURL* downloadURL = [NSURL URLWithString:downloadURLString]; NSURLRequest *request = [NSURLRequest requestWithURL:downloadURL]; NSURLSessionDownloadTask *task = [[self backgroundURLSession] downloadTaskWithRequest:request]; task.taskDescription = [NSString stringWithFormat:@"Podcast Episode %d", [contentID intValue]]; //執行resume保證開始了任務 [task resume]; completionHandler(UIBackgroundFetchResultNewData);}
下載完成後調用NSURLSessionDownloadDelegate的委託方法,這些委託方法全部是必須實現的。瞭解所有類型session task的生命週期可以參考官方文檔:https://developer.apple.com/library/ios/documentation/cocoa/Conceptual/URLLoadingSystem/NSURLSessionConcepts/NSURLSessionConcepts.html#//apple_ref/doc/uid/10000165i-CH2-SW42
#Pragma Mark - NSURLSessionDownloadDelegate- (void) URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location{ NSLog(@"downloadTask:%@ didFinishDownloadingToURL:%@", downloadTask.taskDescription, location); // 必須用 NSFileManager 將檔案複製到應用的儲存中,因為臨時檔案在方法返回後會被刪除 // ... // 通知 UI 重新整理}- (void) URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTaskdidResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes{}- (void) URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{}
背景任務完成後如果應用沒有在前台運行,需要實現UIApplication的兩個delegate讓系統喚醒應用
- (void) application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler{ // 你必須重建立立一個後台 seesiong 的參照 // 否則 NSURLSessionDownloadDelegate 和 NSURLSessionDelegate 方法會因為 // 沒有 對 session 的 delegate 設定而不會被調用。參見上面的 backgroundURLSession NSURLSession *backgroundSession = [self backgroundURLSession]; NSLog(@"Rejoining session with identifier %@ %@", identifier, backgroundSession); // 儲存 completion handler 以在處理 session 事件後更新 UI [self addCompletionHandler:completionHandler forSession:identifier];}- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session{ NSLog(@"Background URL session %@ finished events.", session); if (session.configuration.identifier) { // 調用在 -application:handleEventsForBackgroundURLSession: 中儲存的 handler [self callCompletionHandlerForSession:session.configuration.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."); } [self.completionHandlerDictionary setObject:handler forKey: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(); }}
轉載地址:http://www.starming.com/index.php?v=index&view=69
三種方式使得iOS應用能夠在後台進行資料更新和下載