三種方式使得iOS應用可以在後臺進行數據更新和下載

1:Background Fetch,html

2:Remote Notificationios

3:NSURLSession的backgroundSessionConfigurationjson

Background Fetch

開啓

首先在info plist文件中開啓UIBackgroundModes的Background fetch。或者手動編輯這個值session

<key>UIBackgroundModes</key>
<array>
     <string>fetch</string>
</array>

iOS默認不進行background fetch,須要設置一個時間的間隔app

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
     //UIApplicationBackgroundFetchIntervalMinimum表示儘量頻繁去獲取,若是須要指定至少多少時間更新一次就須要給定一個時間值
     [application setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
     return YES;
}

最後在App Delegate裏實現下面的方法,這個方法只能在30秒內完成。ide

- (void) application:(UIApplication *)application
performFetchWithCompletionHandler:(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的通知會調用下面的方法fetch

- (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]建立一個後臺任務,當應用退出後,崩潰或進程被關掉都仍是會運行。ui

範例,先處理一條遠程通知,並將NSURLSessionDownloadTask添加到後臺傳輸服務隊列。url

- (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 *)downloadTask
didResumeAtOffset:(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();
     }
}
相關文章
相關標籤/搜索