iPhone - 背景调查事件

Nei*_*lis 66 iphone location background multitasking

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

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

它在后台运行并跟踪您使用的蜂窝/ WiFi数据的数量.我怀疑开发人员正在注册他的应用程序作为跟踪位置更改但在应用程序运行时看不到位置服务图标,我认为这是一项要求.

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

Jac*_*rse 91

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

如果您使用其中一个后台功能,那么一旦退出(由系统),该应用将再次由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];
    }
}
Run Code Online (Sandbox Code Playgroud)

并且注册在

- (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");
    }
}
Run Code Online (Sandbox Code Playgroud)

现在神奇的事情发生了:我甚至不使用VoIP套接字.但是这10分钟的回调提供了一个很好的副作用:在10分钟之后(有时更早)我发现我的计时器和之前的跑步踏板正在执行一段时间.如果在代码中放置一些NSLog(..),就可以看到这一点.这意味着,这个简短的"唤醒"会执行一段时间的代码.根据Apple的说法,我们还有30秒的执行时间.我假设,像线程这样的后台代码正在执行近30秒.如果您必须"有时"检查某些内容,这是有用的代码.

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

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

现在写一些"任务必须完成"的代码.根据Apple的说法,你还有一段时间(5秒钟?)来完成任务.我发现,这必须是CPU时间.这意味着:如果你什么都不做,你的应用程序仍在执行中!如果你完成了你的工作,Apple建议召唤一个expirationhandler.在下面的代码中,您可以看到,我在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;

    }); 
}
Run Code Online (Sandbox Code Playgroud)

在这里非常节省CPU时间,你的应用程序运行时间更长!但有一点是肯定的:您的应用程序将在一段时间后终止.但是因为您将应用程序注册为VoIP或其他任何一个,系统将在后台重新启动应用程序,这将重新启动后台进程;-)使用此PingPong,我可以进行大量的后台处理.但请记住,CPU时间非常闲暇.并保存所有数据,以恢复您的视图 - 您的应用程序将在一段时间后终止.为了让它看起来仍在运行,你必须在唤醒后跳回到你的最后一个"状态".

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

希望我能提供帮助

更新:

在测量了BG任务的时间后,出人意料.BG任务限制为600秒.这是VoIP最短时间的确切最短时间(setKeepAliveTimeout:600).

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

标题:

UIBackgroundTaskIdentifier bgTask; 
Run Code Online (Sandbox Code Playgroud)

码:

// 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);
        }    
    }); 
}
Run Code Online (Sandbox Code Playgroud)

应用程序超时后,将调用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     
    }); 
}
Run Code Online (Sandbox Code Playgroud)

没有 VoIP黑客工作.根据文档,如果执行时间结束,将执行到期处理程序(在这种情况下,我的'expirationHandler'块).通过将块定义为块变量,可以在到期处理程序中再次递归地启动长时间运行的任务.这也导致无休止的执行.

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

根据我自己的经验,我测量了一些东 使用GPS收音机打开的位置回调非常快地吸收我的电池.使用我在Update 2中发布的方法几乎没有能量.根据"userexperience",这是一种更好的方法.也许其他应用程序的工作方式如下,隐藏了GPS功能背后的行为......

  • 此解决方案间歇性地工作,并且取决于每个iOS版本如何实现后台应用程序的暂停.例如,我相信它在iOS 6.0中有效,但在6.1版本中停止工作.同样地,我让它在7.0中工作,然后它在7.1中再次爆发,因为苹果公司在再次管理电池消耗方面变得更加积极.所以,简而言之,它并不可靠. (4认同)
  • 嗨,这取决于你的编码.看看最后一个例子.10秒后,如果你没有终止你的bgtask,框架将终止任务.你的bghandler(提供的块)将终止bgtask.在块中,正在启动一个新的bgtask.在我的应用程序中工作.我发现,你应该在你的头文件中使块变量"全局"并在启动时初始化它.如果您使用两次初始化块变量,则应用程序将被终止. (2认同)

ben*_*ree 17

什么工作,什么不工作

目前还不完全清楚这些答案中的哪一个起作用,而且我浪费了很多时间来尝试它们.所以这是我对每个策略的经验:

  1. VOIP hack - 有效,但如果您不是VOIP应用程序,被拒绝
  2. 递归beginBackgroundTask...- 不起作用.它会在10分钟后退出.即使您在评论中尝试修复(至少截至2012年11月30日的评论).
  3. Silent Audio - 有效,但是人们因此被拒绝了
  4. 本地/推送通知 - 在您的应用被唤醒之前需要用户互动
  5. 使用背景位置 - 工作.以下是详细信息:

基本上,您使用"位置"后台模式来保持您的应用程序在后台运行.它确实有效,即使用户不允许位置更新.即使用户按下主页按钮并启动另一个应用程序,您的应用程序仍将运行.它也是一个电池滤水器,如果您的应用程序与位置无关,可能会在批准过程中延伸,但据我所知,这是唯一一个很有可能获得批准的解决方案.

以下是它的工作原理:

在你的plist集合中:

  • 应用程序不在后台运行:NO
  • 所需的背景模式:位置

然后引用CoreLocation框架(在Build Phases中)并在应用程序的某个地方添加此代码(在进入后台之前):

#import <CoreLocation/CoreLocation.h>

CLLocationManager* locationManager = [[CLLocationManager alloc] init];
[locationManager startUpdatingLocation];
Run Code Online (Sandbox Code Playgroud)

注意:startMonitoringSignificantLocationChanges不会工作.

值得一提的是,如果您的应用程序崩溃,那么iOS将无法恢复生命.VOIP黑客是唯一可以带回来的黑客.

  • 在我的测试中,`startMonitoringSignificantLocationChanges`将在后台运行,但不会阻止应用程序在一段时间后进入休眠状态. (2认同)

小智 6

还有另一种技术永远留在后台 - 在后台任务中启动/停止位置管理器,将在调用didUpdateToLocation:重置后台计时器.

我不知道它为什么会起作用,但我认为didUpdateToLocation也被称为任务,从而重置了计时器.

基于测试,我相信这就是DataMan Pro所使用的.

看到这篇文章/sf/answers/452569631/我从哪里获得技巧.

以下是我们的应用程序的一些结果:

2012-02-06 15:21:01.520 **[1166:4027] BGTime left: 598.614497 
2012-02-06 15:21:02.897 **[1166:4027] BGTime left: 597.237567 
2012-02-06 15:21:04.106 **[1166:4027] BGTime left: 596.028215 
2012-02-06 15:21:05.306 **[1166:4027] BGTime left: 594.828474 
2012-02-06 15:21:06.515 **[1166:4027] BGTime left: 593.619191
2012-02-06 15:21:07.739 **[1166:4027] BGTime left: 592.395392 
2012-02-06 15:21:08.941 **[1166:4027] BGTime left: 591.193865 
2012-02-06 15:21:10.134 **[1166:4027] BGTime left: 590.001071
2012-02-06 15:21:11.339 **[1166:4027] BGTime left: 588.795573
2012-02-06 15:21:11.351 **[1166:707] startUpdatingLocation
2012-02-06 15:21:11.543 **[1166:707] didUpdateToLocation
2012-02-06 15:21:11.623 **[1166:707] stopUpdatingLocation
2012-02-06 15:21:13.050 **[1166:4027] BGTime left: 599.701993
2012-02-06 15:21:14.286 **[1166:4027] BGTime left: 598.465553
Run Code Online (Sandbox Code Playgroud)


Ste*_*ton 1

如果不是GPS,我认为唯一的其他方法就是背景音乐功能,即在启用时一直播放4"33"。两者听起来都有点滥用后台处理 API,因此可能会受到审查过程的突发奇想的影响。

  • “在启用时一直播放 4 分 33 秒”这是我一整天读到的最好的东西。 (7认同)