iOS App后台保活 您所在的位置:网站首页 cad中各种标高缩写代表什么符号 iOS App后台保活

iOS App后台保活

2023-09-22 03:20| 来源: 网络整理| 查看: 265

级别:★☆☆☆☆ 标签:「iOS App 后台保活」「BackgroundTasks」「后台下载资源」 作者: WYW 审校: QiShare团队

前段时间,笔者和GY哥一起吃饭聊天的时候,GY哥问了笔者一个问题,iOS App 可以后台保活吗?是如何做到后台保活的?当时笔者只想到了可以在后台播放静音的音乐,对于唤醒App,可以考虑使用推送的方式。GY哥提到播放静音文件会影响上线吗?这我就不大知道了…...由于没有相关实践,笔者后来在网上查了相关资料,总结了本文。

笔者查询了相关资料后发现,iOS App可以实现后台保活。

短时间保活的方式有beginBackgroundTaskWithName;

App长时间保活的方式有:播放无声音乐、后台持续定位、后台下载资源、BGTaskScheduler等;

唤醒App的方式有:推送、VoIP等;

本文分为如下几部分:

App 运行状态、及状态变化

App 后台保活方式简介

短时间App后台保活

Background Modes AVAudio,AirPlay,and Picture in Picture

Background Modes Location updates

BGTaskScheduler (iOS13.0+)

App 运行状态、及状态变化 不低于iOS13.0的设备端App 运行状态

不低于iOS13.0设备端App 运行状态

iOS13.0+的设备,支持多场景,共有上图中的Unattached、Foreground Inactive、Foreground Active、Forground Inactive、Background、Suspended 6种状态。

Unattached:多个场景的情况,如果创建的场景不是当前显示的场景,那么场景处于Unattached状态;

Foreground Inactive:应用启动后,显示启动图的过程中,处于Foreground Inactive状态;

Forground Active:应用启动后,显示出来我们设置的rootViewController之后,场景处于Forground Active;

Foreground Inactive:应用启动后,场景处于显示状态,数据加载完毕,且用户和App没有交互过程中,处于Forground Inactive状态;

Background:用户点击Home键、或者是切换App后、锁屏后,应用进入Background状态;

Suspended:进入Background后,应用的代码不执行后,应用进入Suspended状态;(代码是否在运行,可以在应用中写定时器,定时输出内容,从Xcode控制台,或Mac端控制台查看是否有输出内容来判断)

低于iOS13.0的设备端App 运行状态

低于iOS13.0设备端App 运行状态

上图是低于iOS13.0的设备端App的运行状态,分别是Not Running、Foreground Inactive、Foreground Active、Forground Inactive、Background、Suspended 6种状态。

Not Running:指用户没有启动App,或用户Terminate App 后,App处于的状态;其他的五种状态和不低于iOS13.0的设备端App的运行状态意义相同。

App 进入后台状态变化

笔者写了个定时器,定时输出“普通定时器进行中”,可以看到,应用进入后台后,基本上立刻,就没有内容输出了。笔者认为可以认为此时App 已经进入Suspended的状态。

App 进入后台

下边笔者介绍下,尝试的App后台保活方式。

iOS App 后台保活方式简介 短时间App后台保活

beginBackgroundTaskWithName 和 endBackgroundTask

笔者尝试过使用相关API,测试过2款手机。

对于系统版本低于iOS13(iOS 12.3)的设备(iPhone6 Plus)后台运行时间约3分钟(175秒);

对于系统版本不低于iOS13(iOS 13.0)的设备(iPhone6 Plus)后台运行时间约31秒;

播放无声音乐

App 进入后台后,播放无声音乐,适用于音视频类App。

笔者对逆向不了解,从iOS项目技术还债之路《一》后台下载趟坑中得知,腾讯视频、爱奇艺采用了播放无声音乐保活的方式。

后台持续定位

对于定位类App,持续定位App,可以实现App后台保活。定位类App需要后台保活,像系统的地图应用,在导航的时候切换App的时候,就需要后台保活。

后台下载资源

对于需要下载资源的App,需要后台下载资源,如我们在某App下载资源的时候,我们希望在切换App时候,或者App退出后台后,资源仍然继续下载,这样当我们打开App的时候,资源已经下载好了。

BackgroundTasks

BackgroundTasks.framework 是iOS13新增的framework,笔者认为此framework中的API可以在信息流类的App中发挥作用。

短时间App后台保活 系统版本低于iOS13.0的设备

系统版本低于iOS13.0的设备,在应用进入后台的时候,开始后台任务([[UIApplication sharedApplication] beginBackgroundTaskWithName:)。在应用进入前台时或后台任务快过期的回调中,终止后台任务([[UIApplication sharedApplication] endBackgroundTask:)。

示例代码如下:

- (void)applicationDidEnterBackground:(UIApplication *)application { self.backgroundTaskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithName:kBgTaskName expirationHandler:^{ if (self.backgroundTaskIdentifier != UIBackgroundTaskInvalid) { [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskIdentifier]; self.backgroundTaskIdentifier = UIBackgroundTaskInvalid; } }]; } - (void)applicationWillEnterForeground:(UIApplication *)application { [[UIApplication sharedApplication] endBackgroundTask: self.backgroundTaskIdentifier]; }

添加相关代码后,笔者在iOS12.4的6 Plus上测试结果如下,应用在进入后台后,大概还运行了175秒。

2019-12-29 19:06:55.647288+0800 QiAppRunInBackground[1481:409744] -[AppDelegate applicationDidEnterBackground:]:应用进入后台DidEnterBackground

2019-12-29 19:06:56.256877+0800 QiAppRunInBackground[1481:409744] 定时器运行中

….

2019-12-29 19:09:50.812460+0800 QiAppRunInBackground[1481:409744] 定时器运行中

系统版本不低于iOS13.0的设备 - (void)sceneDidEnterBackground:(UIScene *)scene API_AVAILABLE(ios(13.0)){ self.backgroundTaskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithName:kBgTaskName expirationHandler:^{ if (self.backgroundTaskIdentifier != UIBackgroundTaskInvalid) { [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskIdentifier]; self.backgroundTaskIdentifier = UIBackgroundTaskInvalid; } }]; } - (void)sceneWillEnterForeground:(UIScene *)scene API_AVAILABLE(ios(13.0)){ [[UIApplication sharedApplication] endBackgroundTask: self.backgroundTaskIdentifier]; }

添加相关代码后,笔者在iOS13.0的6s上测试结果如下,应用在进入后台后,大概还运行了31秒。

iOS13.0+ App 进入后台

Xs·H 提到过,如果持续后台播放无声音频或是使用后台持续定位的方式实现iOS App后台保活,会浪费电量,浪费CPU,所以一般情况下,使用这种短时间延长App 后台保活的方式,应该够开发者做需要的操作了。

Background Modes AVAudio,AirPlay,and Picture in Picture

对于音视频类App,如果需要后台保活App,在App 进入后台后,可以考虑先使用短时间保活App的方式,如果后台保活App方式快结束后,还没处理事情,那么可以考虑使用后台播放无声音乐。相关示例代码如下。

- (AVAudioPlayer *)player { if (!_player) { NSURL *fileURL = [[NSBundle mainBundle] URLForResource:@"SomethingJustLikeThis" withExtension:@"mp3"]; AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:nil]; audioPlayer.numberOfLoops = NSUIntegerMax; _player = audioPlayer; } return _player; } [self.player prepareToPlay]; 系统版本低于iOS13.0的设备 - (void)applicationDidEnterBackground:(UIApplication *)application { NSLog(@"%s:应用进入后台DidEnterBackground", __FUNCTION__); self.backgroundTaskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithName:kBgTaskName expirationHandler:^{ if ([QiAudioPlayer sharedInstance].needRunInBackground) { [[QiAudioPlayer sharedInstance].player play]; } if (self.backgroundTaskIdentifier != UIBackgroundTaskInvalid) { [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskIdentifier]; self.backgroundTaskIdentifier = UIBackgroundTaskInvalid; } }]; } - (void)applicationWillEnterForeground:(UIApplication *)application { NSLog(@"%s:应用将进入前台WillEnterForeground", __FUNCTION__); if ([QiAudioPlayer sharedInstance].needRunInBackground) { [[QiAudioPlayer sharedInstance].player pause]; } [[UIApplication sharedApplication] endBackgroundTask: self.backgroundTaskIdentifier]; } 系统版本不低于iOS13.0的设备 - (void)sceneDidEnterBackground:(UIScene *)scene API_AVAILABLE(ios(13.0)){ NSLog(@"%s:应用已进入后台DidEnterBackground", __FUNCTION__); self.backgroundTaskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithName:kBgTaskName expirationHandler:^{ if (self.backgroundTaskIdentifier != UIBackgroundTaskInvalid) { if ([QiAudioPlayer sharedInstance].needRunInBackground) { [[QiAudioPlayer sharedInstance].player play]; } NSLog(@"终止后台任务"); [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskIdentifier]; self.backgroundTaskIdentifier = UIBackgroundTaskInvalid; } }]; } - (void)sceneWillEnterForeground:(UIScene *)scene API_AVAILABLE(ios(13.0)){ if ([QiAudioPlayer sharedInstance].needRunInBackground) { [[QiAudioPlayer sharedInstance].player pause]; } [[UIApplication sharedApplication] endBackgroundTask: self.backgroundTaskIdentifier]; NSLog(@"%s:应用将进入前台WillEnterForeground", __FUNCTION__); } Background Modes Location updates

开启后台定位持续更新配置,添加了位置隐私申请后,在应用使用持续定位的情况下,可以实现后台保活App。

添加后台获取位置及音频使用能力

添加获取位置隐私申请

对于定位类App,如果需要后台保活App,在用户使用了定位功能后,App 进入后台后,App自动具备后台保活能力,部分示例代码如下。

self.locationManager = [CLLocationManager new]; self.locationManager.delegate = self; [self.locationManager requestAlwaysAuthorization]; @try { self.locationManager.allowsBackgroundLocationUpdates = YES; } @catch (NSException *exception) { NSLog(@"异常:%@", exception); } @finally { } [self.locationManager startUpdatingLocation];

如果遇到如下异常信息:

2019-12-29 19:57:46.481218+0800 QiAppRunInBackground[1218:141397] 异常:Invalid parameter not satisfying: !stayUp || CLClientIsBackgroundable(internal->fClient) || _CFMZEnabled()

检查:Signing&Capablities 的 backgounrd Modes 中 Location updates是否勾选; 后台下载资源

当需要实现下载资源类的App在进入后台后,持续下载资源的需求时。我们可能需要使用后台如下示例示例代码。

创建指定标识的后台NSURLSessionConfiguration,配置好

NSURL *url = [NSURL URLWithString:@"https://images.pexels.com/photos/3225517/pexels-photo-3225517.jpeg"]; NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"com.qishare.ios.wyw.backgroundDownloadTask"]; // 低于iOS13.0设备资源下载完后 可以得到通知 AppDelegate.m 文件中的 - (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)(void))completionHandler // iOS13.0+的设备资源下载完后 直接在下载结束的代理方法中会有回调 sessionConfig.sessionSendsLaunchEvents = YES; // 当传输大数据量数据的时候,建议将此属性设置为YES,这样系统可以安排对设备而言最佳的传输时间。例如,系统可能会延迟传输大文件,直到设备连接充电器并通过Wi-Fi连接到网络为止。 此属性的默认值为NO。 sessionConfig.discretionary = YES; NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:nil]; NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithURL:url]; [downloadTask resume]; BGTaskScheduler(iOS13.0+)

如果我们的App是信息流类App,那么我们可能会使用到BGTaskScheduler.framework中的API,实现后台保活App,帮助用户较早地获取到较新信息。

笔者尝试使用BGTaskScheduler 做了一个获取到App调度的时候。更新首页按钮颜色为随机色并且记录调度时间的Demo。

Demo示意图 项目配置

为了App 支持 BGTaskScheduler,需要在项目中配置Background fetch,及Background Processing;

需要在Info.plist文件中添加 key 为Permitted background task scheduler identifiers,Value为数组的内容。

Value的数组填写,刷新的任务标识和清理的任务标识。

注册后台任务

在应用启动后,注册后台任务。

- (void)registerBgTask { if (@available(iOS 13.0, *)) { BOOL registerFlag = [[BGTaskScheduler sharedScheduler] registerForTaskWithIdentifier:kRefreshTaskId usingQueue:nil launchHandler:^(__kindof BGTask * _Nonnull task) { [self handleAppRefresh:task]; }]; if (registerFlag) { NSLog(@"注册成功"); } else { NSLog(@"注册失败"); } } else { // Fallback on earlier versions } if (@available(iOS 13.0, *)) { [[BGTaskScheduler sharedScheduler] registerForTaskWithIdentifier:kCleanTaskId usingQueue:nil launchHandler:^(__kindof BGTask * _Nonnull task) { [self handleAppRefresh:task]; }]; } else { // Fallback on earlier versions } } 调度App 刷新

应用进入后台后,调度App 刷新。

- (void)sceneDidEnterBackground:(UIScene *)scene API_AVAILABLE(ios(13.0)){ [self scheduleAppRefresh]; } - (void)scheduleAppRefresh { if (@available(iOS 13.0, *)) { BGAppRefreshTaskRequest *request = [[BGAppRefreshTaskRequest alloc] initWithIdentifier:kRefreshTaskId]; // 最早15分钟后启动后台任务请求 request.earliestBeginDate = [NSDate dateWithTimeIntervalSinceNow:15.0 * 60]; NSError *error = nil; [[BGTaskScheduler sharedScheduler] submitTaskRequest:request error:&error]; if (error) { NSLog(@"错误信息:%@", error); } } else { // Fallback on earlier versions } }

得到后台任务调度的时候,调用App刷新的方法,笔者在这个方法中做了发送更新首页按钮颜色的通知,并且记录了当前更新时间的记录。

- (void)handleAppRefresh:(BGAppRefreshTask *)appRefreshTask API_AVAILABLE(ios(13.0)){ [self scheduleAppRefresh]; NSLog(@"App刷新===================================================================="); NSOperationQueue *queue = [NSOperationQueue new]; queue.maxConcurrentOperationCount = 1; NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ [[NSNotificationCenter defaultCenter] postNotificationName:AppViewControllerRefreshNotificationName object:nil]; NSLog(@"操作"); NSDate *date = [NSDate date]; NSDateFormatter *dateFormatter = [NSDateFormatter new]; [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm"]; NSString *timeString = [dateFormatter stringFromDate:date]; NSString *filePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingPathComponent:@"QiLog.txt"]; if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) { NSData *data = [timeString dataUsingEncoding:NSUTF8StringEncoding]; [[NSFileManager defaultManager] createFileAtPath:filePath contents:data attributes:nil]; } else { NSData *data = [[NSData alloc] initWithContentsOfFile:filePath]; NSString *originalContent = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; NSString *content = [originalContent stringByAppendingString:[NSString stringWithFormat:@"\n时间:%@\n", timeString]]; data = [content dataUsingEncoding:NSUTF8StringEncoding]; [data writeToFile:filePath atomically:YES]; } }]; appRefreshTask.expirationHandler = ^{ [queue cancelAllOperations]; }; [queue addOperation:operation]; __weak NSBlockOperation *weakOperation = operation; operation.completionBlock = ^{ [appRefreshTask setTaskCompletedWithSuccess:!weakOperation.isCancelled]; }; }

经过测试,发现App 在退到后台,没有手动Terminate App的情况下。苹果有调用过App调度任务的方法。现象上来看就是隔一段时间,我们再打开App 的时候可以发现,首页的按钮颜色改变了,相应的日志中追加了,调起相关方法的时间记录。

手动触发后台任务调度

Xcode运行我们的App

-> App 退到后台

-> 打开App 进入前台

-> 点击下图中蓝框中的Pause program execution,输入如下内容

后台模拟调起App

e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier: @"com.qishare.ios.wyw.background.refresh"]

-> 再次点击Continue program execution,就可以模拟后台启动任务,调用我们的App。

Continue program execution

查看日志记录小提示

之前记得听沐灵洛提过怎么便于查看日志,正好我这里也用到了。便于我们可以直接在File App中查看写入到我们App的Documents中的文件,可以在Info.plist文件中添加key为LSSupportsOpeningDocumentsInPlace ,value为YES的键值对App 接入 iOS 11 的 Files App。

经过我们操作后,就可以打开File App -> 浏览 -> 我的iPhone -> 查看选择我们的App -> 查看我们的日志记录文件。

示例Demo

QiAppRunInBackground

参考学习网址 BGTaskScheduler Refreshing and Maintaining Your App Using Background Tasks iOS如何实时查看App运行日志 iOS 后台挂起的一些坑 iOS后台运行的相关方案总结 iOS项目技术还债之路《一》后台下载趟坑 iOS之原生地图的使用详解 App 接入 iOS 11 的 Files App

推荐文章: Swift 中使用 CGAffineTransform iOS 性能监控(一)—— CPU功耗监控 iOS 性能监控(二)—— 主线程卡顿监控 iOS 性能监控(三)—— 方法耗时监控 初识Flutter web 用SwiftUI给视图添加动画 用SwiftUI写一个简单页面 iOS App启动优化(三)—— 自己做一个工具监控App的启动耗时 iOS App启动优化(二)—— 使用“Time Profiler”工具监控App的启动耗时 iOS App启动优化(一)—— 了解App的启动流程 奇舞周刊



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有