当应用在后台时,人们如何处理预定的NSTimer?
假设我每小时都在我的应用中更新一些内容。
updateTimer = [NSTimer scheduledTimerWithTimeInterval:60.0*60.0
target:self
selector:@selector(updateStuff)
userInfo:nil
repeats:YES];
在后台时,此计时器显然不会触发(?)。当用户回到应用程序时应该怎么办?计时器是否仍在运行,时间相同?
如果用户在一小时内回来,会发生什么。它会在错过的所有时间触发,还是等到下一个更新时间?
我希望它做的是在应用程序进入前台后立即更新,如果它应该触发的日期是过去的。这可能吗?
答案 0 :(得分:42)
你can在后台执行模式下有计时器触发。有几个技巧:
beginBackgroundTaskWithExpirationHandler
执行后台执行。 [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]
如果你在主线上:
{
// Declare the start of a background task
// If you do not do this then the mainRunLoop will stop
// firing when the application enters the background
self.backgroundTaskIdentifier =
[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskIdentifier];
}];
// Make sure you end the background task when you no longer need background execution:
// [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskIdentifier];
[NSTimer scheduledTimerWithTimeInterval:0.5
target:self
selector:@selector(timerDidFire:)
userInfo:nil
repeats:YES];
}
- (void) timerDidFire:(NSTimer *)timer
{
// This method might be called when the application is in the background.
// Ensure you do not do anything that will trigger the GPU (e.g. animations)
// See: http://developer.apple.com/library/ios/DOCUMENTATION/iPhone/Conceptual/iPhoneOSProgrammingGuide/ManagingYourApplicationsFlow/ManagingYourApplicationsFlow.html#//apple_ref/doc/uid/TP40007072-CH4-SW47
}
备注强>
答案 1 :(得分:38)
您不应该通过设置计时器来解决此问题,因为您不允许在后台执行任何代码。想象一下,如果用户在此期间或其他边缘情况下重启iPhone,将会发生什么。
使用AppDelegate的applicationDidEnterBackground:
和applicationWillEnterForeground:
方法获取所需的行为。它的功能更强大,因为当你的应用程序因重启或内存压力而被完全杀死时,它也会起作用。
当应用程序进入后台时,您可以节省计时器下次触发的时间,并检查应用程序返回前台时是否应该采取措施。也可以在此方法中停止并启动计时器。在您的应用程序运行时,您可以使用计时器在适当的时刻触发更新。
答案 2 :(得分:17)
如果您或其他人正在寻找如何在Swift的后台运行NSTimer,请将以下内容添加到您的App Delegate:
var backgroundUpdateTask: UIBackgroundTaskIdentifier = 0
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
return true
}
func applicationWillResignActive(application: UIApplication) {
self.backgroundUpdateTask = UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler({
self.endBackgroundUpdateTask()
})
}
func endBackgroundUpdateTask() {
UIApplication.sharedApplication().endBackgroundTask(self.backgroundUpdateTask)
self.backgroundUpdateTask = UIBackgroundTaskInvalid
}
func applicationWillEnterForeground(application: UIApplication) {
self.endBackgroundUpdateTask()
}
干杯!
答案 3 :(得分:6)
在iOS 8上,当应用程序处于后台时(即使iphone被锁定),你可以通过一种简单的方式让你的NSTimer正常工作~3分钟:
在您需要的地方照常实施scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:
方法。在AppDelegate内部创建一个属性:
UIBackgroundTaskIdentifier backgroundUpdateTask
然后在你的appDelegate方法中:
- (void)applicationWillResignActive:(UIApplication *)application {
self.backgroundUpdateTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[self endBackgroundUpdateTask];
}];
}
- (void) endBackgroundUpdateTask
{
[[UIApplication sharedApplication] endBackgroundTask: self.backgroundUpdateTask];
self.backgroundUpdateTask = UIBackgroundTaskInvalid;
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
[self endBackgroundUpdateTask];
}
3分钟后计时器不会点火更多,当应用程序在前台重新开始时它将再次开火
答案 4 :(得分:5)
当在后台时,此计时器显然不会触发
This post表明事情并不那么清楚。当应用程序进入后台时,您应该使计时器无效,并在它返回前台时重新启动它们。在后台运行东西可能是可能的,但是再次它可能会让你被杀......
您可以找到有关iOS多任务处理方法here的良好文档。
答案 5 :(得分:3)
您需要在当前运行循环中添加计时器。
[[NSRunLoop currentRunLoop] addTimer:myTimer forMode:NSRunLoopCommonModes];
答案 6 :(得分:2)
[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:nil];
loop = [NSTimer scheduledTimerWithTimeInterval:0.25 target:self selector:@selector(Update) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:loop forMode:NSRunLoopCommonModes];
答案 7 :(得分:1)
您需要在运行循环中添加计时器(Reference - 用于运行循环理解的Apple开发人员页面)。
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self
selector:@selector(updateTimer) userInfo:nil repeats:true];
[[NSRunLoop mainRunLoop] addTimer: timer forMode:NSRunLoopCommonModes];
//funcation
-(void) updateTimer{
NSLog(@"Timer update");
}
您需要在 infoPlist 中添加后台工作的权限(必需的后台模式)。
答案 8 :(得分:0)
创建全局uibackground任务标识符。
{1, 1.00001, 2, 3}
现在创建您的计时器并添加BGTaskIdentifier,不要忘记在创建新的计时器对象时删除旧的BGTaskIdentifier。
UIBackgroundTaskIdentifier bgRideTimerTask;
即使应用程序在后台运行,这里对我仍然有效。如果发现新问题,请问我。
答案 9 :(得分:0)
==============对于目标c ===============
创建全局uibackground任务标识符。
UIBackgroundTaskIdentifier bgRideTimerTask;
现在创建您的计时器并添加BGTaskIdentifier,不要忘记在创建新的计时器对象时删除旧的BGTaskIdentifier。
[timerForRideTime invalidate];
timerForRideTime = nil;
bgRideTimerTask = UIBackgroundTaskInvalid;
UIApplication *sharedApp = [UIApplication sharedApplication];
bgRideTimerTask = [sharedApp beginBackgroundTaskWithExpirationHandler:^{
}];
timerForRideTime = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:@selector(timerTicked:)
userInfo:nil
repeats:YES];
[[NSRunLoop currentRunLoop]addTimer:timerForRideTime forMode: UITrackingRunLoopMode];
即使应用程序在后台运行,这里对我仍然有效。如果发现新问题,请问我。
答案 10 :(得分:0)
对我来说,后台任务和运行循环很关键,而且大多数时候都不准确。
我决定使用UserDefault方法。
步骤1:添加应用输入背景/前景观察者
第2步:当用户进入后台时,将计时器的时间存储在用户默认值中并带有当前时间戳记
第3步:当用户进入前台时,将用户的默认时间戳与当前时间戳进行比较,并计算出新的计时器
全部完成。
代码段:
//添加到viewDidLoad / init函数中
NotificationCenter.default.addObserver(self, selector: #selector(self.background(_:)), name: UIApplication.didEnterBackgroundNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.foreground(_:)), name: UIApplication.willEnterForegroundNotification, object: nil)
//添加到VC / View
let userDefault = UserDefaults.standard
@objc func background(_ notification: Notification) {
userDefault.setValue(timeLeft, forKey: "OTPTimer")
userDefault.setValue(Date().timeIntervalSince1970, forKey: "OTPTimeStamp")
}
@objc func foreground(_ notification: Notification) {
let timerValue: TimeInterval = userDefault.value(forKey: "OTPTimer") as? TimeInterval ?? 0
let otpTimeStamp = userDefault.value(forKey: "OTPTimeStamp") as? TimeInterval ?? 0
let timeDiff = Date().timeIntervalSince1970 - otpTimeStamp
let timeLeft = timerValue - timeDiff
completion((timeLeft > 0) ? timeLeft : 0)
print("timeLeft:", timeLeft) // <- This is what you need
}
答案 11 :(得分:0)
Swift 4+ 版本的 gellieb's answer
var backgroundUpdateTask: UIBackgroundTaskIdentifier = UIBackgroundTaskIdentifier(rawValue: 0)
func applicationWillResignActive(_ application: UIApplication) {
self.backgroundUpdateTask = UIApplication.shared.beginBackgroundTask(expirationHandler: {
self.endBackgroundUpdateTask()
})
}
func endBackgroundUpdateTask() {
UIApplication.shared.endBackgroundTask(self.backgroundUpdateTask)
self.backgroundUpdateTask = UIBackgroundTaskIdentifier.invalid
}
func applicationWillEnterForeground(application: UIApplication) {
self.endBackgroundUpdateTask()
}