很长一段时间以来,我一直在研究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/

10-11 22:16