iPhone - 后台轮询事件 [英] iPhone - Backgrounding to poll for events

查看:40
本文介绍了iPhone - 后台轮询事件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

很长一段时间以来,我一直在我的 iPhone 应用程序中寻找一种方法,每 X 分钟轮询一次以检查数据计数器.在大量阅读后台执行文档和一些试用应用程序后,我认为这在不滥用后台 API 的情况下是不可能的.

For quite a while I'd been looking into a way in my iPhone app to poll every X minutes to check the data counters. After much reading of the Background Execution documentation and a few trial apps I'd dismissed this as impossible without abusing the background APIs.

上周我发现这个应用程序正是这样做的.http://itunes.apple.com/us/app/dataman-real-time-data-usage/id393282873?mt=8

Last week I found this application which does exactly that. http://itunes.apple.com/us/app/dataman-real-time-data-usage/id393282873?mt=8

它在后台运行并跟踪您使用的蜂窝网络/WiFi 数据的数量.我怀疑开发者将他的应用注册为跟踪位置变化,但在应用运行时位置服务图标不可见,我认为这是一个要求.

It runs in the background and keeps track of the count of Cellular/WiFi data you've used. I suspect that the developer is registering his app as tracking location changes but the location services icon isn't visible while the app is running, which I thought was a requirement.

有没有人知道如何实现这一点?

Does anyone have any clues as to how this can be accomplished?

推荐答案

我也见过这种行为.在尝试了很多之后,我发现了两件事,这可能会有所帮助.但我仍然不确定这会如何影响审核过程.

I have seen this behavior, too. After trying a lot I discovered two things, which could help. But I am still uncertain how this may influence the reviewing process.

如果您使用其中一项后台功能,该应用程序将在退出(由系统)后由 iOS 再次在后台启动.我们稍后会滥用这一点.

If you use one of the backgrounding features, the app will be launched by iOS in background again once it was quit (by the system). This we will abuse later.

就我而言,我在 plist 中启用了 VoIP 后台.这里的所有代码都在你的 AppDelegate 中完成:

In my case I used VoIP backgrounding enabled in my plist. All the code here is done in your 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(..) 放入代码中,您就可以看到这一点.这意味着,这个简短的唤醒"会执行代码一段时间.根据 Apple 的说法,我们还有 30 秒的执行时间.我假设像线程这样的后台代码被执行了将近 30 秒.这是有用的代码,如果您必须有时"检查某些内容.

Now the magic happens: I don't even use VoIP-Sockets. But this 10 Minutes callback provides a nice side effect: After 10 Minutes (sometimes earlier) I discovered that my timers and previous running treads are being executed for a short while. You can see this, if you place some NSLog(..) into your code. This means, that this short "wakeup" executes the code for a while. According to Apple we have 30 seconds execution time left. I assume, that background code like threads are being executed for nearly 30 seconds. This is useful code, if you must "sometimes" check something.

该文档说,如果应用程序终止,所有后台任务(VoIP、音频、位置更新)都将在后台自动重新启动.VoIP 应用会在开机后自动在后台启动!

The doc says that all background tasks (VoIP, audio, location updates) will be automatically restarted in background if the app was terminated. VoIP apps will be started in background automatically after bootup!

通过滥用此行为,您可以让您的应用看起来像永远"运行.注册一个后台进程(即 VoIP).这将导致您的应用在终止后重新启动.

With abusing this behavior, you can make your app be looking like running "forever". Register for one background process (i.e. VoIP). This will cause your app to be restarted after termination.

现在编写一些任务必须完成"的代码.根据 Apple 的说法,您还有一些时间(5 秒?)来完成任务.我发现,这一定是 CPU 时间.所以这意味着:如果你什么都不做,你的应用程序仍在执行!如果你完成了你的工作,Apple 建议调用一个 expirehandler.在下面的代码中,您可以看到,我在 expireHandler 上有一条评论.只要系统允许您的应用程序运行,这就会导致您的应用程序运行.所有计时器和线程都会保持运行,直到 iOS 终止您的应用.

Now write some "Task has to be finished" code. According to Apple you have some time (5 seconds?) left to finish tasks. I discovered, that this must be CPU time. So that means: if you do nothing, your app is still being executed! Apple suggest to call an expirationhandler, if you are finished with your work. In the code below you can see, that i have a comment at the expirationHandler. This will cause your app running as long as the system allows your app to be running. All timers and threads stay running until iOS terminates your app.

- (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 时间.并保存所有数据,以恢复您的视图 - 您的应用程序将在一段时间后终止.为了让它看起来仍在运行,你必须在唤醒后跳回到上一个状态".

Be very spare with CPU-Time here, and your app runs longer! But one thing is for sure: your app will be terminated after a while. But because you registered your app as VoIP or one of the others, the system restarts the app in background, which will restart your background process ;-) With this PingPong I can do a lot of backgrounding. but remember be very spare with CPU time. And save all data, to restore your views - your app will be terminated some time later. To make it appear still running, you must jump back into your last "state" after wakeup.

我不知道这是否是您之前提到的应用程序的方法,但它对我有用.

I don't know if this is the approach of the apps you mentioned before, but it works for me.

希望能帮到你

更新:

测完BG任务的时间,有惊喜.BG 任务限制为 600 秒.这是 VoIP 最短时间的确切最短时间 (setKeepAliveTimeout:600).

After measuring the time of the BG task, there was a surprise. The BG Task is limited to 600 seconds. This is the exact minimum time of the VoIP minimumtime (setKeepAliveTimeout:600).

所以这段代码导致在后台无限"执行:

So THIS code leads into "infinite" execution in background:

标题:

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 expireHandler,您只需重新启动一个长时间运行的任务即可.此任务将在 600 秒后终止.但是将再次调用到期处理程序,这将启动另一个长时间运行的任务等.现在您只需检查应用程序返回前台的天气.然后关闭 bgTask,就大功告成了.也许一个人可以做某事.像这样在长时间运行的任务中的 expireHandler 里面.试试吧.使用您的控制台,看看会发生什么...玩得开心!

After your app has timed out, the VoIP expirationHandler will be called, where you simply restart a long running task. This task will be terminated after 600 seconds. But there will be again a call to the expiration handler, which starts another long running task, etc. Now you only have to check weather the App is getting back to foreground. Then close the bgTask, and you're done. Maybe one can do sth. like this inside the expirationHandler from the long running task. Just try it out. Use your Console, to see what happens... Have Fun!

更新 2:

有时简化事情会有所帮助.我的新方法是这样的:

Sometimes simplifying things helps. My new approach is this one:

- (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 黑客的情况下这是有效的.根据文档,如果执行时间结束,将执行到期处理程序(在本例中为我的expirationHandler"块).通过将块定义为块变量,可以在到期处理程序中再次递归启动长时间运行的任务.这也会导致无休止的执行.

This is working without the VoIP hack. According to the documentation, the expiration handler (in this case my 'expirationHandler' block) will be executed if execution time is over. By defining the block into a block variable, one can recursively start the long running task again within the expiration handler. This leads into endless execution, too.

如果您的应用程序再次进入前台,请注意终止任务.如果您不再需要它,请终止该任务.

Be aware to terminate the task, if your application enters foreground again. And terminate the task if you don't need it anymore.

根据我自己的经验,我测量了一些东西.在 GPS 无线电开启的情况下使用位置回调会很快消耗我的电池电量.使用我在更新 2 中发布的方法几乎不需要任何精力.根据用户体验",这是一种更好的方法.也许其他应用程序会像这样工作,将其行为隐藏在 GPS 功能背后......

For my own experience I measured something. Using the location callbacks with having the GPS radio on is sucking my battery down very quickly. Using the approach which I posted in Update 2 is taking nearly no energy. According to the "userexperience" this is a better approach. Maybe other Apps work like this, hiding its behavior behind GPS functionality ...

这篇关于iPhone - 后台轮询事件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆