美文网首页iOS开发心得iOS进阶指南iOS Tips
三个方法使得iOS程序在关闭或者崩溃状态下能够在后台持续进行一些

三个方法使得iOS程序在关闭或者崩溃状态下能够在后台持续进行一些

作者: 星光社的戴铭 | 来源:发表于2015-09-06 17:06 被阅读1175次

    三种方式使得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 *)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的通知会调用下面的方法

    - (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 *)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.\n", 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.\n");
         }
    
         [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();
         }
    }
    

    相关文章

      网友评论

      • zero000:您好,我想问下后台获取是不是只有程序在后台才会起作用,那么用户如果把程序退出,要做什么处理呢?

      本文标题:三个方法使得iOS程序在关闭或者崩溃状态下能够在后台持续进行一些

      本文链接:https://www.haomeiwen.com/subject/yaircttx.html