iOS App后台任务

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Q52077987/article/details/82768522

Background Task

因为iOS程序切换到后台之后,很有可能被系统杀掉,因此切换到后台的时候需要保存重要数据。UIApplication的这个方法能让系统给App一段时间,执行重要任务。

beginBackgroundTaskWithName:expirationHandler:

系统给了多少时间呢?可以用backgroundTimeRemaining API查看。如果超时,expirationHandler参数的block将会被调用(不超时就不会调用)。

-(void)applicationDidEnterBackground:(UIApplication *)application{
    __block UIBackgroundTaskIdentifier taskId = [[UIApplication sharedApplication] beginBackgroundTaskWithName:@"fakeTask" expirationHandler:^{
        NSLog(@"end");
    }];
    
    dispatch_async(dispatch_get_main_queue(), ^{
        
        for (int i = 0; i < 10000; i++){
            if(i % 10 == 0){
                UIApplicationState state = [[UIApplication sharedApplication] applicationState];
                NSTimeInterval timeLeft = [[UIApplication sharedApplication] backgroundTimeRemaining];
                NSLog(@"App Status: %ld, task time left: %.2f", (long)state, timeLeft);
            }

            NSLog(@"counting: %d", i);
        }
        
        [[UIApplication sharedApplication] endBackgroundTask:taskId];
        taskId = UIBackgroundTaskInvalid;
    });
}

需要使用endBackgroundTask来显示的结束task,否则任务就一定会超时,当然超时的时候会执行expirationHandler,但是最后程序会被杀死。

如果不在Background的状态下调用beginBackgroundTaskWithName函数呢?会正常执行。

Background Download

使用URLSession可以让应用程序进行后台下载,即使App被系统杀死了,后台下载仍能顺利完成。

首先需要将URLSession的Config配置成Background

-(NSURLSession *)session{
    if(_session == nil){
        NSURLSessionConfiguration *conf = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"bk_download"];
        conf.sessionSendsLaunchEvents = YES;
        conf.discretionary = YES;
        conf.networkServiceType = NSURLNetworkServiceTypeBackground;
        
        _session = [NSURLSession sessionWithConfiguration:conf delegate:self delegateQueue:nil];
    }
    
    return _session;
}

Background 类型的Session不支持Block形式的Task,要使用delegate

-(void)downloadFileInBackground{
    NSString *urlStr = @"http://dg.101.hk/1.rar";
    NSURL *url = [NSURL URLWithString:urlStr];
    NSURLSessionTask *backgroundTask = [self.session downloadTaskWithURL:url];
    backgroundTask.earliestBeginDate = [[NSDate date] dateByAddingTimeInterval:10];
    [backgroundTask resume];
}

然后在delegate中写下载完成的回调,这一步和前台的下载一样:

-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location{
//...
}

后台下载完成的回调

-(void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)(void))completionHandler{
    NSLog( @"handleEventsForBackgroundURLSession");
    self.downloadHandler = completionHandler;
	self.session; //如果app被系统杀死,要重新创建session。
}

handleEventsForBackgroundURLSession这个回调函数无论App在后台还是被系统杀死,都会调用,如果App被系统杀死了,则要重建URLSession(必须用同一个id)。在这个函数中还要将completionHandler保存起来,在所有任务都完成后,调用这个handler告诉系统,已经处理完成。

-(void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session{
    NSLog(@"URLSessionDidFinishEventsForBackgroundURLSession");
    if(self.downloadHandler){
        self.downloadHandler();
    }
}

当所有任务执行完毕后,会回到URLSessionDidFinishEventsForBackgroundURLSession这个函数,在这里告诉系统执行完毕吗,可以被Suspend了。

如果程序是在前台,则下载完成不会调用handleEventsForBackgroundURLSession和URLSessionDidFinishEventsForBackgroundURLSession。

同样,用户主动杀死的程序不能使用后台下载功能。

测试时,如何让系统杀死应用程序?
https://forums.developer.apple.com/thread/92687#280376

Background Fetch

Background Fetch可以让App没有启动或者在后台的时候,周期性的获取数据。一些内容类型的App,比如新闻,小说等,可以通过使用Background Fetch技术让用户更快的获取信息。一个最合适的场景描述是:一个新闻App,通过Background Fetch技术在夜里获取了用户关注的最新内容并保存到本地,早上用户在地铁等信号不好的地方打开App,可以直接查看本地保存的最新新闻。

使用Background Fetch功能需要申请Capability:
在这里插入图片描述
第二步,在App启动的时候设置请求周期

[[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];

第三步,实现AppDelegate的application:performFetchWithCompletionHandler:方法

-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(nonnull void (^)(UIBackgroundFetchResult))completionHandler{
    
    // 请求数据
    completionHandler(UIBackgroundFetchResultNewData);
}

这个方法的执行时间不要超过30秒,实际上越短越好,如果这个方法执行时间过长,iOS系统就会降低Background Fetch的调用频率。

When this method is called, your app has up to 30 seconds of wall-clock time to perform the download operation and call the specified completion handler block.

调试设置
在这里插入图片描述
将launch due to background fetch event勾上,然后点击Run按钮启动应用。

注意,Background Fetch不会工作,如果用户主动杀死了App。

https://stackoverflow.com/questions/35478726/background-fetch-is-not-working-after-killing-the-app

Remote Notification

在iOS7以后推送消息的时候可以唤醒App,执行一段代码,也叫静默推送。静默推送和普通推送的流程有些差别:
在这里插入图片描述
在这里插入图片描述
普通推送,系统收到推送消息之后,应用程序并不做任何事情,等待用户操作。静默推送,在系统收到推送之后,唤起App,App的didReceiveRemoteNotification方法被调用,在这个方法中可以发起一个网络请求,下载数据。

静默推送只需要在普通推送的payload中增加一个content-available的key:

{
"aps":
	{ 
	"content-available":1,
	"alert":"This is some fancy message2.",
	"badge":6,
	"sound": "default"
	}
}

需要注意的是:

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
    NSLog(@"iOS7及以上系统,收到通知:%@", userInfo);
    completionHandler(UIBackgroundFetchResultNewData);
}

静默推送唤起的任务最多执行30s的时间,completionHandler必须被调用,告诉系统任务执行完毕。

As soon as you finish processing the notification, you must call the block in the handlerparameter or your app will be terminated. Your app has up to 30 seconds of wall-clock time to process the notification and call the specified completion handler block.

另外,在用户点击了推动消息启动了应用之后,didReceiveNotificationResponse这个方法会被调用,因此要注意数据的处理是否会和didReceiveRemoteNotification重复。

- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler{
    completionHandler(); // 系统要求执行这个方法
}

同样,如果用户主动杀死了App,静默推动也不会起作用。

其他

如何禁止应用程序在后台运行?

在Info.pist中添加Key UIApplicationExitsOnSuspend,这样应用程序就不会进入Background状态,切换到后台会调用applicationWillTerminate:然后结束。

应用程序后台任务完成后如何通知用户?
可以使用LocalNotification技术。对于静默推送,本身已经通知了用户,不需要再次使用LocalNotification技术。对于IM类型的应用程序,使用静默推送和LocalNotification组合可以优化设计。

其他后台运行的情况

当应用程序需要如下功能的时候,往往会在后台运行:Location, voip, audio, bluetooth, newsstand-content,对应的都需要在Capabilities中注册服务。

官方文档:Background Execution

猜你喜欢

转载自blog.csdn.net/Q52077987/article/details/82768522