iOS Background Task使用陷阱
原创 2022-03-10
发现线上用户有在后台状态被杀死的情况,最终定位到是不合理使用Background Task造成的,这篇文章简要介绍下使用Background Task需要注意的事项。
Background Task相关的API如下:
@interface UIApplication : UIResponder
...
@property(nonatomic,readonly) NSTimeInterval backgroundTimeRemaining
- (UIBackgroundTaskIdentifier)beginBackgroundTaskWithExpirationHandler:(void(^ __nullable)(void))handler;
- (void)endBackgroundTask:(UIBackgroundTaskIdentifier)identifier;
...
@end
这里直接给出Background Task一般用法:
@property (assign, nonatomic) UIBackgroundTaskIdentifier backgroundTaskIdentifier;
@property (strong, nonatomic) NSTimer *timer;
/*
进入前台时检查是否有运行中的BackgroundTask,需要及时关闭。
*/
- (void)applicationWillEnterForeground:(UIApplication *)application {
NSLog(@"easeapi:applicationWillEnterForeground");
if (self.backgroundTaskIdentifier != UIBackgroundTaskInvalid) {
[application endBackgroundTask:self.backgroundTaskIdentifier];
self.backgroundTaskIdentifier = UIBackgroundTaskInvalid;
}
}
/*
进入后台时,检查self.backgroundTaskIdentifier == UIBackgroundTaskInvalid才执行beginBackgroundTaskWithExpirationHandler
*/
- (void)applicationDidEnterBackground:(UIApplication *)application {
NSLog(@"easeapi:applicationDidEnterBackground");
//do your background task
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerUpdate:) userInfo:nil repeats:YES];
if (self.backgroundTaskIdentifier != UIBackgroundTaskInvalid) return;
self.backgroundTaskIdentifier = [application beginBackgroundTaskWithExpirationHandler:^(void) {
[self.timer invalidate];
//end background task
[application endBackgroundTask:self.backgroundTaskIdentifier];
self.backgroundTaskIdentifier = UIBackgroundTaskInvalid;
NSLog(@"easeapi:beginBackgroundTaskWithExpirationHandler END");
}];
}
-(void)timerUpdate:(NSTimer *)tim {
NSLog(@"TIMER :%f", [UIApplication sharedApplication].backgroundTimeRemaining);
}
Background Task几点使用原则:
Background Task仅用于执行短时间的任务
注意和Background Mode的区别。启用Background Mode后(音频播放、后台定位、VoIP等),是没有后台时间限制的。
而Background Task仅用于执行短时间的任务。APP切换到后台后,可以通过beginBackgroundTaskWithExpirationHandler申请一段时间的后台时间,你的任务应该在这段时间内执行完成,否则可能会被系统杀死。
如果不确定任务的执行时间或者是文件读写需要依赖重度IO的操作,不建议使用Background Task。
Background Task最长持续时间到底是多长?
通过以下方式获取后台任务的剩余时间:
NSTimeInterval timeRemaining = [UIApplication sharedApplication].backgroundTimeRemaining
在前台获取时backgroundTimeRemaining为-1(最大值)。切换到后台后backgroundTimeRemaining为后台任务的剩余时间。
Background Task的持续时间并不是一个固定值,在不同性能的设备上差别巨大。在iPhone 6上为20多秒,在高性能的设备上则达到180秒。测试时发现,最长持续时间似乎还和当前的资源占用情况有关。
由于在不同设备上Background Task最长持续时间是不同的,所以开发者应该确保在低性能设备上Background Task的可用时间内可以完成后台任务,否则可能被系统杀死。
beginBackgroundTask和endBackgroundTask必须成对出现
@property (assign, nonatomic) UIBackgroundTaskIdentifier backgroundTaskIdentifier;
如果使用全局的UIBackgroundTaskIdentifier记录后台任务,需要注意每次执行beginBackgroundTask都会生成新的UIBackgroundTaskIdentifier。
self.backgroundTaskIdentifier = [application beginBackgroundTaskWithExpirationHandler:^(void) {
}];
旧的UIBackgroundTaskIdentifier会被覆盖,则上一个UIBackgroundTaskIdentifier就没有机会执行endBackgroundTask。此时会出现beginBackgroundTask和endBackgroundTask不配对的情况,可能会被系统杀死。
为防止不配对的情况发生,可以仅维护一个全局UIBackgroundTaskIdentifier,在进入前台时检查不为UIBackgroundTaskInvalid,则执行endBackgroundTask。进入后台时,检查不为UIBackgroundTaskInvalid则直接返回。这样,可以保证频繁的切换前后台时,也能保证beginBackgroundTask和endBackgroundTask配对出现。
多次调用beginBackgroundTask并不会获得更多后台时间
在beginBackgroundTask的ExpirationHandler回调中再次执行beginBackgroundTask不会获取更多后台时间。再次执行beginBackgroundTask后,可以获取一个UIBackgroundTaskIdentifier,但此时无法继续执行后台任务了。
相关文章:
iOS APP灰度发布方案
iOS crash log分析实践
LLDB命令速查手册
iOS:清除Xcode缓存
iOS系统如何获取用户的本机手机号