很长一段时间以来,我一直在研究iPhone应用程序中每隔X分钟轮询一次以检查数据计数器的方法。在仔细阅读了后台执行文档和一些试用版应用程序之后,我认为如果不滥用后台API,这是不可能的。
上周,我发现了该应用程序。 http://itunes.apple.com/us/app/dataman-real-time-data-usage/id393282873?mt=8
它在后台运行,并跟踪您使用的蜂窝/ WiFi数据的计数。我怀疑开发人员正在将其应用程序注册为跟踪位置更改,但在应用程序运行时看不到位置服务图标,我认为这是必需的。
有人对如何实现这一目标有任何线索吗?
最佳答案
我也看到了这种行为。尝试了很多之后,我发现了两件事,可以有所帮助。但是我仍然不确定这将如何影响审核过程。
如果您使用其中一种后台功能,则该应用程序一旦退出(由系统退出),iOS就会再次在后台启动该应用程序。我们稍后会滥用。
就我而言,我使用了在plist中启用的VoIP背景。
这里的所有代码都在您的AppDelegate中完成:
// if the iOS device allows background execution,
// this Handler will be called
- (void)backgroundHandler {
NSLog(@"### -->VOIP backgrounding callback");
// try to do sth. According to Apple we have ONLY 30 seconds to perform this Task!
// Else the Application will be terminated!
UIApplication* app = [UIApplication sharedApplication];
NSArray* oldNotifications = [app scheduledLocalNotifications];
// Clear out the old notification before scheduling a new one.
if ([oldNotifications count] > 0) [app cancelAllLocalNotifications];
// Create a new notification
UILocalNotification* alarm = [[[UILocalNotification alloc] init] autorelease];
if (alarm)
{
alarm.fireDate = [NSDate date];
alarm.timeZone = [NSTimeZone defaultTimeZone];
alarm.repeatInterval = 0;
alarm.soundName = @"alarmsound.caf";
alarm.alertBody = @"Don't Panic! This is just a Push-Notification Test.";
[app scheduleLocalNotification:alarm];
}
}
并且注册在
- (void)applicationDidEnterBackground:(UIApplication *)application {
// This is where you can do your X Minutes, if >= 10Minutes is okay.
BOOL backgroundAccepted = [[UIApplication sharedApplication] setKeepAliveTimeout:600 handler:^{ [self backgroundHandler]; }];
if (backgroundAccepted)
{
NSLog(@"VOIP backgrounding accepted");
}
}
现在魔术发生了:我什至不使用VoIP套接字。但这10分钟的回调提供了一个很好的副作用:10分钟(有时更早)之后,我发现我的计时器和以前的跑步踏板正在执行一会儿。如果将一些NSLog(..)放入代码中,则可以看到此内容。这意味着,这种短暂的“唤醒”将执行代码一段时间。根据苹果公司的说法,我们还有30秒的执行时间。我认为,像线程这样的后台代码正在执行将近30秒。如果您必须“有时”检查某些内容,则这是有用的代码。
该文档说,如果该应用程序终止,则所有后台任务(VoIP,音频,位置更新)将在后台自动重启。 VoIP应用程序将在启动后自动在后台启动!
通过滥用此行为,您可以使您的应用看起来像在“永远”运行。
注册一个后台进程(即VoIP)。这将导致您的应用在终止后重新启动。
现在编写一些“任务必须完成”代码。根据苹果公司的说法,您还有一些时间(5秒?)来完成任务。我发现这一定是CPU时间。这意味着:如果您什么也不做,则您的应用仍在执行!如果您完成工作,Apple建议致电过期处理程序。在下面的代码中,您可以看到,我在expirationHandler上有一条注释。只要系统允许您的应用程序运行,这就会导致您的应用程序运行。在iOS终止您的应用程序之前,所有计时器和线程将保持运行状态。
- (void)applicationDidEnterBackground:(UIApplication *)application {
UIApplication* app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
// Start the long-running task and return immediately.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// you can do sth. here, or simply do nothing!
// All your background treads and timers are still being executed
while (background)
[self doSomething];
// This is where you can do your "X minutes" in seconds (here 10)
sleep(10);
}
// And never call the expirationHandler, so your App runs
// until the system terminates our process
//[app endBackgroundTask:bgTask];
//bgTask = UIBackgroundTaskInvalid;
});
}
在这里非常节省CPU时间,并且您的应用程序运行时间更长!但是可以肯定的是:您的应用将在一段时间后终止。但是,因为您将应用程序注册为VoIP或其他应用程序之一,所以系统会在后台重新启动该应用程序,这将重新启动您的后台进程;-)
有了这个PingPong,我可以做很多背景工作。但请记住要非常节省CPU时间。并保存所有数据,以恢复您的 View -您的应用将在一段时间后终止。为了使其看起来仍在运行,您必须在唤醒后跳回到上一个“状态”。
我不知道这是否是您前面提到的应用程序的方法,但是它对我有用。
希望我能帮上忙
更新:
在测量完BG任务的时间后,出现了一个惊喜。 BG任务限制为600秒。这是VoIP最短时间(setKeepAliveTimeout:600)的确切最短时间。
因此,此代码导致在后台执行“无限”执行:
header :
UIBackgroundTaskIdentifier bgTask;
码:
// if the iOS device allows background execution,
// this Handler will be called
- (void)backgroundHandler {
NSLog(@"### -->VOIP backgrounding callback");
UIApplication* app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
// Start the long-running task
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
while (1) {
NSLog(@"BGTime left: %f", [UIApplication sharedApplication].backgroundTimeRemaining);
[self doSomething];
sleep(1);
}
});
- (void)applicationDidEnterBackground:(UIApplication *)application {
BOOL backgroundAccepted = [[UIApplication sharedApplication] setKeepAliveTimeout:600 handler:^{ [self backgroundHandler]; }];
if (backgroundAccepted)
{
NSLog(@"VOIP backgrounding accepted");
}
UIApplication* app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
// Start the long-running task
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
while (1) {
NSLog(@"BGTime left: %f", [UIApplication sharedApplication].backgroundTimeRemaining);
[self doSomething];
sleep(1);
}
});
}
应用程序超时后,将调用VoIP expirationHandler,您只需在其中重新启动长时间运行的任务即可。此任务将在600秒后终止。但是,将再次调用到期处理程序,这将启动另一个长期运行的任务,依此类推。现在,您只需检查应用程序恢复到前台的天气即可。然后关闭bgTask,您已完成。也许可以做某事。在长时间运行的任务的expirationHandler内部,像这样。尝试一下。使用您的控制台,看看会发生什么...玩得开心!
更新2:
有时简化事情会有所帮助。我的新方法是这样的:
- (void)applicationDidEnterBackground:(UIApplication *)application {
UIApplication* app = [UIApplication sharedApplication];
// it's better to move "dispatch_block_t expirationHandler"
// into your headerfile and initialize the code somewhere else
// i.e.
// - (void)applicationDidFinishLaunching:(UIApplication *)application {
//
// expirationHandler = ^{ ... } }
// because your app may crash if you initialize expirationHandler twice.
dispatch_block_t expirationHandler;
expirationHandler = ^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
bgTask = [app beginBackgroundTaskWithExpirationHandler:expirationHandler];
};
bgTask = [app beginBackgroundTaskWithExpirationHandler:expirationHandler];
// Start the long-running task and return immediately.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// inform others to stop tasks, if you like
[[NSNotificationCenter defaultCenter] postNotificationName:@"MyApplicationEntersBackground" object:self];
// do your background work here
});
}
这可以在没有VoIP hack的情况下工作。根据文档,如果执行时间结束,则将执行到期处理程序(在本例中为“expirationHandler”块)。通过将块定义为块变量,可以在到期处理程序中递归地再次启动长时间运行的任务。这也导致无休止的执行。
如果您的应用程序再次进入前台,请注意终止任务。如果您不再需要它,则终止该任务。
根据我自己的经验,我测量了一些东西。
在GPS radio 打开的情况下使用位置回调会很快耗尽电池电量。使用我在Update 2中发布的方法几乎不需要消耗任何精力。根据“用户体验”,这是一种更好的方法。也许其他应用程序是这样工作的,将其行为隐藏在GPS功能后面...
关于iphone - iPhone-轮询事件背景,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/4656214/