93

我正在编写一个需要高精度和低频率的后台位置更新的应用程序。解决方案似乎是一个后台 NSTimer 任务,它启动位置管理器的更新,然后立即关闭。之前有人问过这个问题:

如何在我的 iOS 应用程序中每 n 分钟更新一次后台位置?

应用程序进入后台后每 n 分钟获取一次用户位置

iOS 不是典型的后台位置跟踪计时器问题

具有“位置”背景模式的 iOS 长时间运行的后台计时器

基于位置追踪的iOS全时后台服务

但我还没有得到一个最低限度的例子。在尝试了上述接受的答案的每一种排列之后,我把一个起点放在一起。进入后台:

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    self.bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
        NSLog(@"ending background task");
        [[UIApplication sharedApplication] endBackgroundTask:self.bgTask];
        self.bgTask = UIBackgroundTaskInvalid;
    }];


    self.timer = [NSTimer scheduledTimerWithTimeInterval:60
                                                  target:self.locationManager
                                                selector:@selector(startUpdatingLocation)
                                                userInfo:nil
                                                 repeats:YES];
}

和委托方法:

- (void)locationManager:(CLLocationManager *)manager 
    didUpdateToLocation:(CLLocation *)newLocation 
           fromLocation:(CLLocation *)oldLocation {

    NSLog(@"%@", newLocation);

    NSLog(@"background time: %f", [UIApplication sharedApplication].backgroundTimeRemaining);
    [self.locationManager stopUpdatingLocation];

}

当前的行为是backgroundTimeRemaining从 180 秒递减到零(在记录位置时),然后执行到期处理程序并且不会生成进一步的位置更新。如何修改上述代码以无限期地在后台接收定期位置更新

更新:我的目标是 iOS 7,似乎有一些证据表明后台任务的行为不同:

从后台任务启动 iOS 7 中的位置管理器

4

9 回答 9

62

似乎这stopUpdatingLocation就是触发后台看门狗计时器的原因,所以我将其替换为didUpdateLocation

[self.locationManager setDesiredAccuracy:kCLLocationAccuracyThreeKilometers];
[self.locationManager setDistanceFilter:99999];

这似乎有效地关闭了 GPS。然后背景的选择器NSTimer变为:

- (void) changeAccuracy {
    [self.locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
    [self.locationManager setDistanceFilter:kCLDistanceFilterNone];
}

我所做的只是每隔几分钟定期切换精度以获得高精度坐标,并且由于locationManager尚未停止,因此backgroundTimeRemaining保持在最大值。这将我设备上的电池消耗从每小时约 10%(kCLLocationAccuracyBest在后台保持恒定)减少到每小时约 2%。

于 2013-09-30T02:13:54.330 回答
46

我确实使用位置服务编写了一个应用程序,应用程序必须每 10 秒发送一次位置。而且效果很好。

只需按照 Apple 的文档使用“ allowDeferredLocationUpdatesUntilTraveled:timeout ”方法。

步骤如下:

必需:为更新位置注册后台模式。

1.创建 LocationManger 和 startUpdatingLocation,准确度和过滤距离随心所欲:

-(void) initLocationManager    
{
    // Create the manager object
    self.locationManager = [[[CLLocationManager alloc] init] autorelease];
    _locationManager.delegate = self;
    // This is the most important property to set for the manager. It ultimately determines how the manager will
    // attempt to acquire location and thus, the amount of power that will be consumed.
    _locationManager.desiredAccuracy = 45;
    _locationManager.distanceFilter = 100;
    // Once configured, the location manager must be "started".
    [_locationManager startUpdatingLocation];
}

2.为了让应用程序在后台使用“allowDeferredLocationUpdatesUntilTraveled:timeout”方法永远运行,您必须在应用程序移动到后台时使用新参数重新启动updateLocation,如下所示:

- (void)applicationWillResignActive:(UIApplication *)application {
     _isBackgroundMode = YES;

    [_locationManager stopUpdatingLocation];
    [_locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
    [_locationManager setDistanceFilter:kCLDistanceFilterNone];
    _locationManager.pausesLocationUpdatesAutomatically = NO;
    _locationManager.activityType = CLActivityTypeAutomotiveNavigation;
    [_locationManager startUpdatingLocation];
 }

3.应用程序通过“locationManager:didUpdateLocations:”回调正常获取更新位置:

-(void) locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
//  store data
    CLLocation *newLocation = [locations lastObject];
    self.userLocation = newLocation;

   //tell the centralManager that you want to deferred this updatedLocation
    if (_isBackgroundMode && !_deferringUpdates)
    {
        _deferringUpdates = YES;
        [self.locationManager allowDeferredLocationUpdatesUntilTraveled:CLLocationDistanceMax timeout:10];
    }
}

4.但是你应该在“locationManager:didFinishDeferredUpdatesWithError:”回调中处理数据以达到你的目的

- (void) locationManager:(CLLocationManager *)manager didFinishDeferredUpdatesWithError:(NSError *)error {

     _deferringUpdates = NO;

     //do something 
}

5. 注意:我认为每次应用程序在后台/前台模式之间切换时,我们都应该重置 LocationManager 的参数。

于 2014-07-10T02:18:05.997 回答
39
  1. 如果你UIBackgroundModes的 plist 中有locationkey ,那么你不需要使用beginBackgroundTaskWithExpirationHandler方法。那是多余的。此外,您使用不正确(请参阅此处),但由于您的 plist 已设置,这没有实际意义。

  2. UIBackgroundModes locationplist 中,应用程序将继续无限期地在后台运行,只要CLLocationManger正在运行。如果您stopUpdatingLocation在后台调用,则应用程序将停止并且不会再次启动。

    也许你可以在打电话beginBackgroundTaskWithExpirationHandler之前打电话stopUpdatingLocation,然后在打电话之后startUpdatingLocation你可以打电话endBackgroundTask来让它在 GPS 停止时保持后台运行,但我从来没有尝试过 - 这只是一个想法。

    另一种选择(我没有尝试过)是让位置管理器在后台运行,但一旦你获得准确的位置,将desiredAccuracy属性更改为 1000m 或更高,以允许 GPS 芯片关闭(以节省电池)。然后 10 分钟后,当您需要再次更新位置时,将desiredAccuracy背面更改为 100m 以打开 GPS,直到您获得准确的位置,重复。

  3. 当您致电startUpdatingLocation位置经理时,您必须给它时间来获得职位。你不应该立即打电话stopUpdatingLocation。我们让它运行最多 10 秒,或者直到我们得到一个非缓存的高精度位置。

  4. 您需要过滤掉缓存的位置并检查您获得的位置的准确性,以确保它满足您要求的最低准确性(请参阅此处)。您获得的第一个更新可能是 10 分钟或 10 天前的。您获得的第一个精度可能是 3000m。

  5. 考虑改用重要的位置更改 API。收到重大更改通知后,您可以开始CLLocationManager几秒钟以获得高精度位置。我不确定,我从未使用过重要的位置更改服务。

于 2013-09-27T13:02:25.527 回答
10

当需要启动定位服务和停止后台任务时,应该延迟停止后台任务(1秒应该足够了)。否则定位服务将无法启动。此外,定位服务应保持开启几秒钟(例如 3 秒钟)。

有一个 cocoapod APScheduledLocationManager允许每隔n秒以所需的位置精度获取后台位置更新。

let manager = APScheduledLocationManager(delegate: self)
manager.startUpdatingLocation(interval: 170, acceptableLocationAccuracy: 100)

该存储库还包含一个用 Swift 3 编写的示例应用程序。

于 2016-10-29T14:37:41.413 回答
1

试试startMonitoringSignificantLocationChanges:API 怎么样?它肯定会以较低的频率触发,并且准确度相当不错。locationManager此外,它比使用其他API具有更多优势。

有关此 API 的更多信息已在此链接上进行了讨论

于 2013-09-27T05:21:35.397 回答
1

您需要通过在 info.plist 中添加以下键来在应用程序中添加位置更新模式。

<key>UIBackgroundModes</key>
<array>
    <string>location</string>
</array>

didUpdateToLocation方法将被调用(即使您的应用程序在后台)。您可以在此方法调用的基础上执行任何操作

于 2013-09-27T05:28:01.257 回答
0

在 iOS 8 之后,Apple 对 CoreLocation 框架相关的后台获取和更新进行了一些更改。

1) Apple 在应用程序中引入了获取位置更新的 AlwaysAuthorization 请求。

2) Apple 引入了 backgroundLocationupdates 用于在 iOS 中从后台获取位置。

要在后台获取位置,您需要在 Xcode 的功能中启用位置更新。

//
//  ViewController.swift
//  DemoStackLocation
//
//  Created by iOS Test User on 02/01/18.
//  Copyright © 2018 iOS Test User. All rights reserved.
//

import UIKit
import CoreLocation

class ViewController: UIViewController {

    var locationManager: CLLocationManager = CLLocationManager()
    override func viewDidLoad() {
        super.viewDidLoad()
        startLocationManager()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    internal func startLocationManager(){
        locationManager.requestAlwaysAuthorization()
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        locationManager.delegate = self
        locationManager.allowsBackgroundLocationUpdates = true
        locationManager.startUpdatingLocation()
    }

}

extension ViewController : CLLocationManagerDelegate {

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]){
        let location = locations[0] as CLLocation
        print(location.coordinate.latitude) // prints user's latitude
        print(location.coordinate.longitude) //will print user's longitude
        print(location.speed) //will print user's speed
    }

}
于 2018-01-02T12:28:08.483 回答
0

要在应用程序处于后台时使用标准位置服务,您需要首先在目标设置的功能选项卡中打开后台模式,然后选择位置更新。

或者,直接将其添加到Info.plist

<key>NSLocationAlwaysUsageDescription</key>
 <string>I want to get your location Information in background</string>
<key>UIBackgroundModes</key>
 <array><string>location</string> </array>

然后你需要设置 CLLocationManager

目标 C

//The Location Manager must have a strong reference to it.
_locationManager = [[CLLocationManager alloc] init];
_locationManager.delegate = self;
//Request Always authorization (iOS8+)
if ([_locationManager respondsToSelector:@selector(requestAlwaysAuthorization)]) { [_locationManager requestAlwaysAuthorization];
}
//Allow location updates in the background (iOS9+)
if ([_locationManager respondsToSelector:@selector(allowsBackgroundLocationUpdates)]) { _locationManager.allowsBackgroundLocationUpdates = YES;
}
[_locationManager startUpdatingLocation];

迅速

self.locationManager.delegate = self
if #available (iOS 8.0,*) {
    self.locationManager.requestAlwaysAuthorization()
}
if #available (iOS 9.0,*) {
    self.locationManager.allowsBackgroundLocationUpdates = true
}
self.locationManager.startUpdatingLocation()

参考:https ://medium.com/@javedmultani16/location-service-in-the-background-ios-942c89cd99ba

于 2019-08-24T22:23:37.353 回答
-3
locationManager.allowsBackgroundLocationUpdates = true
于 2019-01-10T07:36:14.780 回答