iOS为啥系统在后台使用位置杀死应用程序

Posted

技术标签:

【中文标题】iOS为啥系统在后台使用位置杀死应用程序【英文标题】:iOS why system kills the app using location in backgroundiOS为什么系统在后台使用位置杀死应用程序 【发布时间】:2016-11-08 10:09:20 【问题描述】:

我有一个应用程序在前台和后台使用位置更新。使用 CoreLocation 框架,我实现了该应用程序,以便在每 5 分钟后将位置更新发送到服务器,使用 this 代码作为参考。

这在前台运行良好,但是当应用程序进入后台时,它会在 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
    )


【问题讨论】:

【参考方案1】:

当您的应用进入后台状态时,切换到significant location updates,您的应用将不断收到位置更新。我认为你可以在CLLocationManger 的对象上调用startMonitoringSignificantLocationChanges。而且我认为您也不需要建立后台任务。

检查Apple Documentation,它指出,

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

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

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

您可以参考this so post和this so post。

希望这会有所帮助:)

【讨论】:

【参考方案2】:

你有没有崩溃日志。如果应用程序由于一些隐藏的错误而没有终止,你应该怀疑内存压力。我想这篇文章会引导你找到突然终止的原因

https://www.raywenderlich.com/23704/demystifying-ios-application-crash-logs

【讨论】:

8badf00d 以“应用程序启动、终止或响应系统事件的时间过长”而闻名。正如我从登录线程 2 中了解到的那样,在主线程上执行的时间太长,它会导致终止 如果您的应用程序经常在后台运行,为什么不考虑使用后台模式。我认为它比后台任务更好,并且在后台保持更长时间。【参考方案3】:

正如@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 确保取消你不使用的任何东西,因为这是非常昂贵的视图(并且有泄漏的错误)。

【讨论】:

【参考方案4】:

另外请注意,您需要注册才能接收后台位置更新。可能不清楚的是,这与“一直在后台运行,随心所欲”不同。这意味着在不同的时间,系统会自行决定向您发送位置数据,您必须尽快处理该位置数据,然后再返回。当系统选择时,您将再次被呼叫。可能有其他位置服务正在运行。系统尝试通过合并所有不同位置的客户端来优化这一点。

后台任务,就像您尝试使用的那样,是针对一个完全不同的问题。当用户退出应用程序时,他们会请求“一点额外时间”来完成某些事情。诸如清理数据库之类的东西。如果你让他们继续跑,你会被杀的。查看您当前的代码,它看起来实际上并没有做任何事情,因为它看起来像是在一次调用后就跳出了循环。但是任何调用NSThread.sleepForTimeInterval(1) 的代码在iOS 中几乎肯定是不正确的。几乎没有理由打这个电话。但您不需要后台任务来管理位置更新。

【讨论】:

【参考方案5】:

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

最近,我正在处理相同的要求。经过2-3周的努力,我做到了。对于其他用户,我为它创建了一个帮助类。在位置跟踪运行之前,我的应用永远不会被操作系统杀死。

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

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

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

优势:

如果位置管理器当前处于 正在运行。

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

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

低内存消耗(单例类)

【讨论】:

以上是关于iOS为啥系统在后台使用位置杀死应用程序的主要内容,如果未能解决你的问题,请参考以下文章

为啥即使我在后台请求位置更新,我的 iOS 应用程序也会被杀死?

即使应用程序在 ios 中被杀死或从后台删除,如何继续更新我的位置?

iOS CoreLocation 在后台

ios 7中的重大位置更改事件-后台服务调用

如何知道我的应用程序被杀死的原因

即使在应用程序被杀死后如何继续进行 iOS 位置跟踪?