当应用程序后台运行时,NSTimer 是不是会触发?
Posted
技术标签:
【中文标题】当应用程序后台运行时,NSTimer 是不是会触发?【英文标题】:Is NSTimer expected to fire when app is backgrounded?当应用程序后台运行时,NSTimer 是否会触发? 【发布时间】:2011-07-08 10:44:47 【问题描述】:我完全不明白,但我的应用程序中的NSTimer
肯定是在后台运行的。我在计时器运行的方法中有一个NSLog
,它在后台运行时正在记录。它在带有 ios 4.2.1 的 iPhone 4 上。我已经在 Info.plist 中声明了位置背景支持。
我在这里和其他地方阅读了文档和许多讨论,这应该是不可能的。这是一个iOS错误吗?还是未记录的功能?我不想使用它并在不久的将来发现,例如随着 iOS 4.3 的到来,Apple 默默地“修复”了它并且应用程序将无法运行。
有人知道吗?
【问题讨论】:
【参考方案1】:NSTimer
将在主运行循环运行时触发。据我所知,Apple 没有做出任何取消计划计时器或阻止主运行循环运行的承诺。当您移动到后台时,您有责任取消安排您的计时器并释放资源。苹果不会为你做这件事。但是,它们可能会因为你不应该跑步或使用太多秒而杀死你。
系统中有许多漏洞允许应用在未经授权的情况下运行。操作系统要防止这种情况会非常昂贵。但你不能依赖它。
【讨论】:
好的,这对我来说已经足够了。谢谢。我不知道我是否错过了什么。我肯定不会使用它。 我正在观察你所说的行为。我使用计时器每 10 分钟刷新一次令牌。如果应用程序在后台运行 15 分钟,然后在前台运行,则计时器将触发。 我没想到会这样。以为计时器不再在后台运行,什么也没有发生。似乎不知何故,所有未触发/未失效的计时器都由运行循环强烈存储,并且如果通过了fireDate
,那么它们就会立即被触发。那么我可以安全地依靠 fireDate
获得通过还是我应该存储一个日期并与之进行比较?【参考方案2】:
您can 在后台执行模式下触发了计时器。有几个技巧:
您需要使用beginBackgroundTaskWithExpirationHandler
选择进入后台执行。
如果在后台线程上创建 NSTimer,则需要手动将其添加到 mainRunLoop。
- (void)viewDidLoad
// Avoid a retain cycle
__weak ViewController * weakSelf = self;
// 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.backgroundIdentifier];
];
// Make sure you end the background task when you no longer need background execution:
// [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskIdentifier];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
// Since we are not on the main run loop this will NOT work:
[NSTimer scheduledTimerWithTimeInterval:0.5
target:self
selector:@selector(timerDidFire:)
userInfo:nil
repeats:YES];
// This is because the |scheduledTimerWithTimeInterval| uses
// [NSRunLoop currentRunLoop] which will return a new background run loop
// which will not be currently running.
// Instead do this:
NSTimer * timer =
[NSTimer timerWithTimeInterval:0.5
target:weakSelf
selector:@selector(timerDidFire:)
userInfo:nil
repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:timer
forMode:NSDefaultRunLoopMode];
// or use |NSRunLoopCommonModes| if you want the timer to fire while scrolling
);
- (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
NSLog(@"Timer did fire");
备注
应用只能在后台执行大约 10 分钟 - 在此之后计时器将停止触发。 从 iOS 7 开始,当设备锁定时,它几乎会立即暂停前台应用程序。锁定 iOS 7 应用后,计时器不会触发。【讨论】:
代码非常平静,这将帮助我完成我的应用程序,THNX :)。一个小问题,“当应用程序在后台时可能会调用此方法”是什么意思?我想用它在 3 小时内从服务器更新一次数据。有更好的方法吗? 您的应用程序在进入后台后最多只能运行 10 分钟。从 iOS 7 开始,这 10 分钟可能是不相交的。您或许可以使用 iOS7 的新后台模式:后台传输用于大容量上传/下载或静默推送以远程唤醒您的应用。 此代码示例表明应用程序本身的前台/后台状态与主队列与全局队列问题之间存在混淆。无需将定时器的调度分派到全局队列。只需像往常一样在主队列上安排计时器,beginBackgroundTaskWithExpirationHandler
将使应用程序保持活动几分钟。
@Rob 我让它发射 180 秒,而无需将其包裹在 beginBackgroundTaskWithExpirationHandler
...您建议使用 beginBackgroundTaskWithExpirationHandler
的任何原因?
当您运行附加到 Xcode 调试器的应用程序时,您可能正在这样做,这会改变应用程序的生命周期。在未连接到 Xcode 的情况下运行它(例如,退出 Xcode 并直接从设备运行),您会发现应用程序被挂起的速度更快(除非您执行此后台任务)。以上是关于当应用程序后台运行时,NSTimer 是不是会触发?的主要内容,如果未能解决你的问题,请参考以下文章