iOS为什么会在后台使用位置时终止应用程序?

5
我有一个应用程序,无论在前台还是后台都使用位置更新。我使用 CoreLocation 框架实现了应用程序,以便每 5 分钟将位置更新发送到服务器,使用代码作为参考。
这在前台下工作得很好,但是当应用转到后台时,操作系统会在 30 分钟到一小时后关闭它。我希望应用程序能在后台至少获取 8 小时的更新。
此外,该应用程序每小时使用约 10% 的电池电量。这是否与应用程序在后台被关闭有关?如果是这样,那么我该如何解决电池问题?否则,有人能告诉我问题出在哪里吗?
以下是设备的崩溃日志:
Exception Type:  00000020
Exception Codes: 0x000000008badf00d
Exception Note:  SIMULATED (this is NOT a crash)
Highlighted by Thread:  2

Application Specific Information:
<BKNewProcess: 0x17e74840; com.app.app; pid: 560; hostpid: -1> has active assertions beyond permitted time: 
{(
<BKProcessAssertion: 0x17d78740> id: 560-C9E81E97-90D9-4F95-871E-3DC53372F302 name: Called by UIKit, from <redacted> process: <BKNewProcess: 0x17e74840; com.app.example; pid: 560; hostpid: -1> permittedBackgroundDuration: 180.000000 reason: finishTask owner pid:560 preventSuspend  preventIdleSleep  preventSuspendOnSleep ,
<BKProcessAssertion: 0x17e6a870> id: 560-BD7B29FC-DABC-42FF-AF17-B277BDB1C59D name: Called by UIKit, from <redacted> process: <BKNewProcess: 0x17e74840; com.app.example; pid: 560; hostpid: -1> permittedBackgroundDuration: 180.000000 reason: finishTask owner pid:560 preventSuspend  preventIdleSleep  preventSuspendOnSleep 
)}

对于后台任务,我使用以下函数:

func backgroundTask(){
    var application=UIApplication.sharedApplication()
    var background_task: UIBackgroundTaskIdentifier?
    background_task = application.beginBackgroundTaskWithExpirationHandler({() -> Void in
        application.endBackgroundTask(background_task!)
        background_task = UIBackgroundTaskInvalid
    })
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {() -> Void in
        //run the app without startUpdatingLocation. backgroundTimeRemaining decremented from 600.00
        self.locationManager.startUpdatingLocation()
        while (true) {
            //backgroundTimeRemaining time does not go down.
            print("Background time Remaining: \(UIApplication.sharedApplication().backgroundTimeRemaining)")
            NSThread.sleepForTimeInterval(1)

            break
            //wait for 1 sec
        }
        application.endBackgroundTask(background_task!)
        background_task = UIBackgroundTaskInvalid
    })

}
5个回答

3

当你的应用程序进入后台状态时,请切换到 significant location updates,这样你的应用程序将持续接收位置更新。我想你可以在 CLLocationManger 的对象上调用 startMonitoringSignificantLocationChanges 方法。而且我认为你不需要建立后台任务。

请查看 苹果文档,它说:

如果您启动此服务并随后终止应用程序,则系统会在出现新事件时自动将应用程序重新启动到后台。在这种情况下,传递给应用程序委托的 application:willFinishLaunchingWithOptions:application:didFinishLaunchingWithOptions: 方法的选项字典包含键 UIApplicationLaunchOptionsLocationKey,以指示您的应用程序是因为位置事件而启动的。重新启动后,您仍然必须配置一个位置管理器对象并调用此方法以继续接收位置事件。当您重新启动位置服务时,当前事件会立即传递给您的委托。此外,即使在开始位置服务之前,位置管理器对象的位置属性也会填充最近的位置对象。

所以,我认为它将解决你的问题,并且也会解决电池问题。

第二个事情(关于电池消耗),当想要长时间在后台更新位置时,不应将DesiredAccuracy设置为kCLLocationAccuracyBest。您可以将kCLLocationAccuracyThreeKilometers设置为DesiredAccuracy,并在进入后台时将setDistanceFilter设置为非常大的数字,例如99999

您可以参考this so postthis so post

希望这能帮到您 :)


1

8badf00d以“应用程序启动、终止或响应系统事件时间过长”而闻名。从线程2的日志中我理解到,某些操作在主线程上执行时间过长,导致了终止。 - Ali Kıran
如果您的应用程序经常在后台运行,为什么不考虑使用后台模式呢?我认为这比后台任务更好,并且可以更长时间地保持在后台。 - Ali Kıran

1

如@Lion所说,在后台输入时使用显著位置变化。我使用SLC时遇到了同样的问题,您也会遇到。当应用程序进入后台时,由于内存警告,系统将其终止。因此,我创建了一个CoreLocation的单例,它将接收CoreLocation的所有委托调用。

为了在服务被杀死的情况下重新启动后台服务:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
 if ([launchOptions objectForKey:UIApplicationLaunchOptionsLocationKey]) {

        //NSLog(@"Restarting SCL");
        LocationService *loc = [LocationService sharedInstance];
        [loc setManagedObjectContext:self.managedObjectContext];
}

LocationService是我的单例。

同时在AppDelegate中实现此函数以处理内存警告通知:

-(void)applicationDidReceiveMemoryWarning:(UIApplication *)application
{
    //Sending notification to every controller to free some memory
    [[NSNotificationCenter defaultCenter] postNotificationName:@"freeMemory" object:nil userInfo:nil];

    SDImageCache *imageCache = [SDImageCache sharedImageCache];
    [imageCache clearMemory];
    [imageCache clearDisk];

    //NSLog(@"Received memory warning!");
}

例如清除图像缓存。 此外,如果您正在使用MapView,请确保将不需要的内容置空,因为这是非常昂贵的视图(并且容易出现泄漏问题)。

0

我发布这个答案是因为每个人都说这是操作系统的默认行为,我们无法改变它……等等。

最近,我也有同样的需求。经过两三周的努力,我做到了。对于其他用户,我创建了一个帮助类。只要位置跟踪在运行,我的应用程序就不会被操作系统杀死。

使用HSLocationManager在活动和非活动状态下进行无限位置跟踪。

请参考我的应用程序,该应用程序可在应用商店中获得(如果位置跟踪正在运行,则应用程序永远不会被操作系统杀死)

位置管理器允许每n秒获取后台位置更新,并具有所需的位置精度。

优点:

  • 如果位置管理器当前正在运行,则操作系统永远不会杀死我们的应用程序。

  • 在需要时定期提供位置更新(范围在2-170秒之间(受最大允许的后台任务时间限制))

  • 可自定义位置精度和时间段。

  • 低内存消耗(单例类)


0

另外需要注意的是,您需要注册以接收后台位置更新。可能不清楚的是,这与“一直在后台运行并随意执行任何操作”不同。这意味着,在系统自行决定的各个时间点,您将收到位置数据,您必须尽快处理该位置数据,然后返回。当系统选择时,会再次调用您。可能还有其他位置服务正在运行。系统试图通过合并所有不同的位置客户端来优化此过程。

像您正在尝试使用的这种后台任务是完全不同的问题。它们是为了在用户退出应用程序时请求“一点额外的时间”来完成某些任务,例如清理数据库等。如果让它们继续运行,您的应用程序将被杀死。查看您当前的代码,似乎它实际上并没有做任何事情,因为它看起来在一次调用后就跳出了循环。但是,在iOS中,几乎没有理由调用NSThread.sleepForTimeInterval(1)。但是,您不应该需要一个后台任务来管理位置更新。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接