如何每隔n分钟在iOS应用程序中获取后台位置更新?

| 我正在寻找一种方法来在我的iOS应用程序中每隔n分钟获取一次后台位置更新。我正在使用iOS 4.3,该解决方案应该适用于非越狱的iPhone。 我尝试/考虑了以下选项:
CLLocationManager startUpdatingLocation/startMonitoringSignificantLocationChanges
:根据配置的属性,它可以在后台正常工作,但是似乎无法强迫它每n分钟更新一次位置
NSTimer
:当应用程序在前台运行但似乎不是为后台任务设计的时,它可以工作 本地通知:可以每n分钟安排一次本地通知,但是无法执行一些代码来获取当前位置(无需用户通过通知启动应用程序)。这种方法似乎也不是一种干净的方法,因为这不是通知的用途。
UIApplication:beginBackgroundTaskWithExpirationHandler
:据我了解,当应用程序移至后台时,应使用它在后台完成一些工作(时间也受限制),而不是执行“长时间运行”后台进程。 如何实施这些常规的后台位置更新?     
已邀请:
        我找到了一个在Apple开发者论坛的帮助下实现此目的的解决方案: 指定
location background mode
UIApplication:beginBackgroundTaskWithExpirationHandler:
在背景中创建
NSTimer
n
小于
UIApplication:backgroundTimeRemaining
时,它将正常工作。当
n
较大时,应在没有剩余时间避免后台任务被杀死之前再次启用(和禁用)and9ѭ。 之所以可行,是因为位置是后台执行的三种允许类型之一。 注意:我通过在不起作用的模拟器中进行测试来浪费了一些时间。但是,它可以在我的手机上正常工作。     
        在iOS 8/9/10上,要使背景位置每5分钟更新一次,请执行以下操作: 转到项目->功能->后台模式->选择位置更新 转到项目->信息->添加具有空值(或可选的任何文本)的键NSLocationAlwaysUsageDescription 要在您的应用程序处于后台运行时使位置正常工作,并每5分钟将坐标发送到Web服务或对它们执行任何操作,请像下面的代码中那样实现它。 我没有使用任何后台任务或计时器。我已经在装有iOS 8.1的设备上测试了此代码,该设备在我的应用程序在后台运行的情况下躺在我的办公桌上了几个小时。设备已锁定,并且代码始终正常运行。
@interface LocationManager () <CLLocationManagerDelegate>
@property (strong, nonatomic) CLLocationManager *locationManager;
@property (strong, nonatomic) NSDate *lastTimestamp;

@end

@implementation LocationManager

+ (instancetype)sharedInstance
{
    static id sharedInstance = nil;

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[self alloc] init];
        LocationManager *instance = sharedInstance;
        instance.locationManager = [CLLocationManager new];
        instance.locationManager.delegate = instance;
        instance.locationManager.desiredAccuracy = kCLLocationAccuracyBest; // you can use kCLLocationAccuracyHundredMeters to get better battery life
        instance.locationManager.pausesLocationUpdatesAutomatically = NO; // this is important
    });

    return sharedInstance;
}

- (void)startUpdatingLocation
{
    CLAuthorizationStatus status = [CLLocationManager authorizationStatus];

    if (status == kCLAuthorizationStatusDenied)
    {
        NSLog(@\"Location services are disabled in settings.\");
    }
    else
    {
        // for iOS 8
        if ([self.locationManager respondsToSelector:@selector(requestAlwaysAuthorization)])
        {
            [self.locationManager requestAlwaysAuthorization];
        }
        // for iOS 9
        if ([self.locationManager respondsToSelector:@selector(setAllowsBackgroundLocationUpdates:)])
        {
            [self.locationManager setAllowsBackgroundLocationUpdates:YES];
        }

        [self.locationManager startUpdatingLocation];
    }
}

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
    CLLocation *mostRecentLocation = locations.lastObject;
    NSLog(@\"Current location: %@ %@\", @(mostRecentLocation.coordinate.latitude), @(mostRecentLocation.coordinate.longitude));

    NSDate *now = [NSDate date];
    NSTimeInterval interval = self.lastTimestamp ? [now timeIntervalSinceDate:self.lastTimestamp] : 0;

    if (!self.lastTimestamp || interval >= 5 * 60)
    {
        self.lastTimestamp = now;
        NSLog(@\"Sending current location to web service.\");
    }
}

@end
    
        我在我正在开发的应用程序中做到了这一点。当应用程序在后台运行但应用程序不断接收位置更新时,计时器不起作用。我在文档中的某处读到了(我现在似乎找不到它,我将在以后发布更新),该方法只能在应用程序在后台运行时在活动运行循环中调用。即使在bg中,应用程序委托也有一个活动的运行循环,因此您无需创建自己的循环即可执行此工作。 [我不确定这是否是正确的解释,但这就是我从阅读中所了解的方式] 首先,在应用程序的info.plist中为键
UIBackgroundModes
添加
location
对象。现在,您需要做的是在应用程序中的任何位置启动位置更新:
    CLLocationManager locationManager = [[CLLocationManager alloc] init];
    locationManager.delegate = self;//or whatever class you have for managing location
    [locationManager startUpdatingLocation];
接下来,编写一种方法来处理位置更新, 在应用程序委托中说
-(void)didUpdateToLocation:(CLLocation*)location
。然后在启动位置管理器的类中实现
CLLocationManagerDelegate
的方法
locationManager:didUpdateLocation:fromLocation
(因为我们将位置管理器委托设置为\'self \')。在此方法内,您需要检查必须经过的时间间隔(必须在该时间间隔之后)。您可以通过每次保存当前时间来做到这一点。如果该时间已过,请从您的应用程序委托中调用方法UpdateLocation:
NSDate *newLocationTimestamp = newLocation.timestamp;
NSDate *lastLocationUpdateTiemstamp;

int locationUpdateInterval = 300;//5 mins

NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
if (userDefaults) {

        lastLocationUpdateTiemstamp = [userDefaults objectForKey:kLastLocationUpdateTimestamp];

        if (!([newLocationTimestamp timeIntervalSinceDate:lastLocationUpdateTiemstamp] < locationUpdateInterval)) {
            //NSLog(@\"New Location: %@\", newLocation);
            [(AppDelegate*)[UIApplication sharedApplication].delegate didUpdateToLocation:newLocation];
            [userDefaults setObject:newLocationTimestamp forKey:kLastLocationUpdateTimestamp];
        }
    }
}
即使您的应用程序处于后台,这也会每5分钟调用一次您的方法。 小知识:此实现会消耗掉电池电量,如果您的位置数据的准确性不是很关键,则应使用ѭ18 将其添加到您的应用之前,请阅读位置感知编程指南     
        现在,iOS6成为永久运行位置服务的最佳方法是...
- (void)applicationWillResignActive:(UIApplication *)application
{
/*
 Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
 Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
 */

NSLog(@\"to background\");

app.isInBackground = TRUE;

UIApplication *app = [UIApplication sharedApplication];

// Request permission to run in the background. Provide an
// expiration handler in case the task runs long.
NSAssert(bgTask == UIBackgroundTaskInvalid, nil);

bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
    // Synchronize the cleanup call on the main thread in case
    // the task actually finishes at around the same time.
    dispatch_async(dispatch_get_main_queue(), ^{

        if (bgTask != UIBackgroundTaskInvalid)
        {
            [app endBackgroundTask:bgTask];
            bgTask = UIBackgroundTaskInvalid;
        }
    });
}];

// Start the long-running task and return immediately.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

    // Do the work associated with the task.

    locationManager.distanceFilter = 100;
    locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;
    [locationManager startMonitoringSignificantLocationChanges];
    [locationManager startUpdatingLocation];

    NSLog(@\"App staus: applicationDidEnterBackground\");
    // Synchronize the cleanup call on the main thread in case
    // the expiration handler is fired at the same time.
    dispatch_async(dispatch_get_main_queue(), ^{
        if (bgTask != UIBackgroundTaskInvalid)
        {
            [app endBackgroundTask:bgTask];
            bgTask = UIBackgroundTaskInvalid;
        }
    });
});

NSLog(@\"backgroundTimeRemaining: %.0f\", [[UIApplication sharedApplication] backgroundTimeRemaining]);

}
只是这样测试: 我启动了该应用程序,进入后台并在车上行驶了几分钟。然后我回家1个小时,然后再次开始移动(无需再次打开该应用程序)。位置再次开始。然后停了两个小时,然后重新开始。一切还好... 不要忘记在iOS6中使用新的定位服务
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{   
    CLLocation *loc = [locations lastObject];

    // Lat/Lon
    float latitudeMe = loc.coordinate.latitude;
    float longitudeMe = loc.coordinate.longitude;
}
    
对于其他有恶梦的人,请找出这一点。我有一个简单的解决方案。 在raywenderlich.com上查看此示例->具有示例代码,这可以正常工作,但是不幸的是,在后台定位期间没有计时器。这将无限期地运行。 使用添加计时器:
-(void)applicationDidEnterBackground {
[self.locationManager stopUpdatingLocation];

UIApplication*    app = [UIApplication sharedApplication];

bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
    [app endBackgroundTask:bgTask];
    bgTask = UIBackgroundTaskInvalid;
}];

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

}
只是不要忘记在info.plist中添加“用于位置更新的应用程序注册”。     
        这是我用的:
import Foundation
import CoreLocation
import UIKit

class BackgroundLocationManager :NSObject, CLLocationManagerDelegate {

    static let instance = BackgroundLocationManager()
    static let BACKGROUND_TIMER = 150.0 // restart location manager every 150 seconds
    static let UPDATE_SERVER_INTERVAL = 60 * 60 // 1 hour - once every 1 hour send location to server

    let locationManager = CLLocationManager()
    var timer:NSTimer?
    var currentBgTaskId : UIBackgroundTaskIdentifier?
    var lastLocationDate : NSDate = NSDate()

    private override init(){
        super.init()
        locationManager.delegate = self
        locationManager.desiredAccuracy = kCLLocationAccuracyKilometer
        locationManager.activityType = .Other;
        locationManager.distanceFilter = kCLDistanceFilterNone;
        if #available(iOS 9, *){
            locationManager.allowsBackgroundLocationUpdates = true
        }

        NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.applicationEnterBackground), name: UIApplicationDidEnterBackgroundNotification, object: nil)
    }

    func applicationEnterBackground(){
        FileLogger.log(\"applicationEnterBackground\")
        start()
    }

    func start(){
        if(CLLocationManager.authorizationStatus() == CLAuthorizationStatus.AuthorizedAlways){
            if #available(iOS 9, *){
                locationManager.requestLocation()
            } else {
                locationManager.startUpdatingLocation()
            }
        } else {
                locationManager.requestAlwaysAuthorization()
        }
    }
    func restart (){
        timer?.invalidate()
        timer = nil
        start()
    }

    func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
        switch status {
        case CLAuthorizationStatus.Restricted:
            //log(\"Restricted Access to location\")
        case CLAuthorizationStatus.Denied:
            //log(\"User denied access to location\")
        case CLAuthorizationStatus.NotDetermined:
            //log(\"Status not determined\")
        default:
            //log(\"startUpdatintLocation\")
            if #available(iOS 9, *){
                locationManager.requestLocation()
            } else {
                locationManager.startUpdatingLocation()
            }
        }
    }
    func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {

        if(timer==nil){
            // The locations array is sorted in chronologically ascending order, so the
            // last element is the most recent
            guard let location = locations.last else {return}

            beginNewBackgroundTask()
            locationManager.stopUpdatingLocation()
            let now = NSDate()
            if(isItTime(now)){
                //TODO: Every n minutes do whatever you want with the new location. Like for example sendLocationToServer(location, now:now)
            }
        }
    }

    func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
        CrashReporter.recordError(error)

        beginNewBackgroundTask()
        locationManager.stopUpdatingLocation()
    }

    func isItTime(now:NSDate) -> Bool {
        let timePast = now.timeIntervalSinceDate(lastLocationDate)
        let intervalExceeded = Int(timePast) > BackgroundLocationManager.UPDATE_SERVER_INTERVAL
        return intervalExceeded;
    }

    func sendLocationToServer(location:CLLocation, now:NSDate){
        //TODO
    }

    func beginNewBackgroundTask(){
        var previousTaskId = currentBgTaskId;
        currentBgTaskId = UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler({
            FileLogger.log(\"task expired: \")
        })
        if let taskId = previousTaskId{
            UIApplication.sharedApplication().endBackgroundTask(taskId)
            previousTaskId = UIBackgroundTaskInvalid
        }

        timer = NSTimer.scheduledTimerWithTimeInterval(BackgroundLocationManager.BACKGROUND_TIMER, target: self, selector: #selector(self.restart),userInfo: nil, repeats: false)
    }
}
我像这样在AppDelegate中开始跟踪:
BackgroundLocationManager.instance.start()
    
        不幸的是,您的所有假设似乎都是正确的,而且我认为没有办法做到这一点。为了节省电池寿命,iPhone的定位服务是基于移动的。如果手机位于一个位置,则位置服务将看不到它。 当电话收到位置更新信息时,
CLLocationManager
仅会呼叫
locationManager:didUpdateToLocation:fromLocation:
,这仅在三个位置服务(蜂窝塔,gps,wifi)之一感知到更改时才会发生。 其他可能有助于进一步解决方案的问题: 启动和停止服务将导致调用
didUpdateToLocation
委托方法,但是
newLocation
可能具有较旧的时间戳。 区域监控可能会有所帮助 在后台运行时,请注意,可能很难获得Apple批准的“完整” LocationServices支持。据我了解,他们专门为需要后台位置支持的应用程序设计了28英镑的低功耗替代方案,并强烈鼓励开发人员使用此应用程序,除非该应用程序绝对需要它。 祝好运! 更新:这些想法可能现在已经过时。上面的@wjans回答似乎使人们获得了成功。     
        我确实使用定位服务编写了一个应用,该应用必须每10秒发送一次位置。 而且效果很好。 只需按照Apple的文档使用\“ allowDeferredLocationUpdatesUntilTraveled:timeout \”方法即可。 我所做的是: 必需:注册后台模式以更新位置。 1.创建
LocationManger
startUpdatingLocation
,并根据需要选择
accuracy
filteredDistance
-(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
方法运行,必须在应用程序移至后台时使用新参数重新启动
updatingLocation
,如下所示:
- (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:
回调正常获取updatedLocations:
-(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.但是您应该根据自己的目的在then39ѭ回调中处理数据
- (void) locationManager:(CLLocationManager *)manager didFinishDeferredUpdatesWithError:(NSError *)error {

     _deferringUpdates = NO;

     //do something 
}
5.注意:我认为每次应用在背景/地面模式之间切换时,我们都应重置ѭ41的参数。     
        
if ([self.locationManager respondsToSelector:@selector(setAllowsBackgroundLocationUpdates:)]) {
    [self.locationManager setAllowsBackgroundLocationUpdates:YES];
}
自iOS 9起,这对于后台位置跟踪来说是必需的。     
        我使用xs2bush的获取间隔的方法(使用
timeIntervalSinceDate
),并对其进行了扩展。我想确保自己获得了所需的准确度,并且通过保持GPS无线电的开启超过必要水平,也没有耗尽电池电量。 我通过以下设置使位置信息连续运行:
locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers;
locationManager.distanceFilter = 5;
这是一个相对较低的电池消耗。当我准备好获取下一个定期的位置读数时,我首先要检查该位置是否在所需的精度范围内,如果可以,则使用该位置。如果不是,那么我通过以下方法提高准确性:
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters;
locationManager.distanceFilter = 0;
确定我的位置,然后找到位置,然后再次降低精度,以最大程度地减少电池消耗。我已经编写了一个完整的工作示例,还编写了服务器端代码的源代码以收集位置数据,将其存储到数据库中,并允许用户实时查看gps数据或检索和查看以前存储的路由。我有iOS,Android,Windows Phone和Java的客户端。所有客户端都是本地编写的,它们都在后台正常工作。该项目是MIT许可的。 iOS项目针对使用iOS 7基本SDK的iOS6。您可以在此处获取代码。 如果发现任何问题,请在github上提交问题。谢谢。     
        似乎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%     
有一个cocoapod APScheduledLocationManager,它可以每隔n秒以所需的定位精度获取后台位置更新。
let manager = APScheduledLocationManager(delegate: self)
manager.startUpdatingLocation(interval: 170, acceptableLocationAccuracy: 100)
该存储库还包含一个用Swift 3编写的示例应用程序。     
        在iOS 9和watchOS 2.0中,CLLocationManager上有一个新方法,可让您请求当前位置:CLLocationManager:requestLocation()。这将立即完成,然后将位置返回给CLLocationManager委托。 您现在可以使用NSTimer使用此方法每分钟请求一个位置,而不必使用startUpdatingLocation和stopUpdatingLocation方法。 但是,如果要基于与上一个位置之间X米的变化来捕获位置,只需将CLLocationManger的distanceFilter属性设置为X即可调用startUpdatingLocation()。     
        随附基于以下内容的Swift解决方案: 在info.plist中定义
App registers for location updates
保持locationManager一直运行 在剩余的等待时间内,将
kCLLocationAccuracy
在ѭ51for(获取位置的5秒钟)和
ThreeKilometers
之间切换,以免耗尽电池 本示例在前景中每1分钟更新一次位置,在背景中每15分钟更新一次位置。 该示例可与在iOS 7设备上运行的Xcode 6 Beta 6完美配合。 在应用程序委托中(mapView是指向mapView控制器的可选指针)
func applicationDidBecomeActive(application: UIApplication!) {
    if appLaunched! == false { // Reference to mapView used to limit one location update per timer cycle
        appLaunched = true
        var appDelegate = UIApplication.sharedApplication().delegate as AppDelegate
        var window = appDelegate.window
        var tabBar = window?.rootViewController as UITabBarController
        var navCon = tabBar.viewControllers[0] as UINavigationController
        mapView = navCon.topViewController as? MapViewController
    }
    self.startInitialPeriodWithTimeInterval(60.0)
}

func applicationDidEnterBackground(application: UIApplication!) {
    self.startInitialPeriodWithTimeInterval(15 * 60.0)
}

func startInitialPeriodWithTimeInterval(timeInterval: NSTimeInterval) {
    timer?.invalidate() // reset timer
    locationManager?.desiredAccuracy = kCLLocationAccuracyBestForNavigation
    timer = NSTimer.scheduledTimerWithTimeInterval(5.0, target: self, selector: Selector(\"getFirstLocationUpdate:\"), userInfo: timeInterval, repeats: false)
}

func getFirstLocationUpdate(sender: NSTimer) {
    let timeInterval = sender.userInfo as Double
    timer?.invalidate()
    mapView?.canReportLocation = true
    timer = NSTimer.scheduledTimerWithTimeInterval(timeInterval, target: self, selector: Selector(\"waitForTimer:\"), userInfo: timeInterval, repeats: true)
}

func waitForTimer(sender: NSTimer) {
    let time = sender.userInfo as Double
    locationManager?.desiredAccuracy = kCLLocationAccuracyBestForNavigation
    finalTimer = NSTimer.scheduledTimerWithTimeInterval(5.0, target: self, selector: Selector(\"getLocationUpdate\"), userInfo: nil, repeats: false)
}

func getLocationUpdate() {
    finalTimer?.invalidate()
    mapView?.canReportLocation = true
}
在mapView中(locationManager指向AppDelegate中的对象)
override func viewDidLoad() {
    super.viewDidLoad()
    var appDelegate = UIApplication.sharedApplication().delegate! as AppDelegate
    locationManager = appDelegate.locationManager!
    locationManager.delegate = self
    canReportLocation = true
}

  func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
        if canReportLocation! {
            canReportLocation = false
            locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
        } else {
            //println(\"Ignore location update\")
        }
    }
    
        工作代码(完整的逐步代码) 步骤1 转到项目->功能->后台模式->选择位置更新。 转到项目->信息->添加带有可选字符串的键NSLocationAlwaysUsageDescription。 第2步 将此代码添加到AppDelegate.m
@interface AppDelegate ()<CLLocationManagerDelegate>
@property (strong, nonatomic) CLLocationManager *locationManager;
@property (strong, nonatomic) NSTimer *timer;
@end
第三步 将此代码添加到AppDelegate.m中的applicationDidEnterBackground方法中
    - (void)applicationDidEnterBackground:(UIApplication *)application {
        UIApplication *app = [UIApplication sharedApplication];
        __block UIBackgroundTaskIdentifier bgTaskId =
        [app beginBackgroundTaskWithExpirationHandler:^{
            [app endBackgroundTask:bgTaskId];
            bgTaskId = UIBackgroundTaskInvalid;
        }];

        dispatch_async( dispatch_get_main_queue(), ^{
            self.timer = nil;
            [self initTimer];
            [app endBackgroundTask:bgTaskId];
            bgTaskId = UIBackgroundTaskInvalid;
        });
    }

- (void)initTimer {
    if (nil == self.locationManager)
        self.locationManager = [[CLLocationManager alloc] init];

    self.locationManager.delegate = self;
    [self.locationManager requestAlwaysAuthorization];
    [self.locationManager startMonitoringSignificantLocationChanges];
    if (self.timer == nil) {
        self.timer = [NSTimer scheduledTimerWithTimeInterval:0.3
                                                      target:self
                                                    selector:@selector(checkUpdates:)
                                                    userInfo:nil
                                                     repeats:YES];
    }
}

- (void)checkUpdates:(NSTimer *)timer{
    UIApplication *app = [UIApplication sharedApplication];
    double remaining = app.backgroundTimeRemaining;
    if(remaining < 580.0) {
        [self.locationManager startUpdatingLocation];
        [self.locationManager stopUpdatingLocation];
        [self.locationManager startMonitoringSignificantLocationChanges];
    }
}

- (void)locationManager:(CLLocationManager *)manager
    didUpdateToLocation:(CLLocation *)newLocation
           fromLocation:(CLLocation *)oldLocation {
    NSLog(@\"Did Update Location = %f / %f\", [newLocation coordinate].latitude, [newLocation coordinate].longitude);
    [self updateLocationWithLatitude:[newLocation coordinate].latitude andLongitude:[newLocation coordinate].longitude];
    UIApplication*    app = [UIApplication sharedApplication];
    __block UIBackgroundTaskIdentifier bgTask =
    bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
        [app endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    }];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [self initTimer];
    });
}

- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
    [self.locationManager stopUpdatingLocation];
    UIApplication *app = [UIApplication sharedApplication];
    __block UIBackgroundTaskIdentifier bgTask =
    bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
        [app endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    }];
    [self initTimer];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // Do the work associated with the task
    });
}

-(void)updateLocationWithLatitude:(CLLocationDegrees)latitude
                     andLongitude:(CLLocationDegrees)longitude{
//Here you can update your web service or back end with new latitude and longitude
}
    

要回复问题请先登录注册