定期 iOS 后台位置更新

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19042894/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 01:46:13  来源:igfitidea点击:

Periodic iOS background location updates

iosobjective-ccore-locationbackground-taskios-background-mode

提问by pcoving

I'm writing an application that requires background location updates with high accuracy and low frequency. The solution seems to be a background NSTimer task that starts the location manager's updates, which then immediately shuts down. This question has been asked before:

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

How do I get a background location update every n minutes in my iOS application?

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

Getting user location every n minutes after app goes to background

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

iOS Not the typical background location tracking timer issue

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

iOS long-running background timer with "location" background mode

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

iOS full-time background-service based on location tracking

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

but I have yet to get a minimum exampleworking. After trying every permutation of the above accepted answers, I put together a starting point. Entering background:

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

- (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];
}

and the delegate method:

和委托方法:

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

    NSLog(@"%@", newLocation);

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

}

The current behavior is that the backgroundTimeRemainingdecrements from 180 seconds to zero (while logging location), and then the expiration handler executes and no further location updates are generated. How do I modify the above code in order to receive periodic location updates in the background indefinitely?

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

Update: I'm targeting iOS 7 and there appears to be some evidence that background tasks behave differently:

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

Start Location Manager in iOS 7 from background task

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

采纳答案by pcoving

It seems that stopUpdatingLocationis what triggers the background watchdog timer, so I replaced it in didUpdateLocationwith:

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

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

which appears to effectively power down the GPS. The selector for the background NSTimerthen becomes:

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

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

All I'm doing is periodically toggling the accuracy to get a high-accuracy coordinate every few minutes and because the locationManagerhasn't been stopped, backgroundTimeRemainingstays at its maximum value. This reduced battery consumption from ~10% per hour (with constant kCLLocationAccuracyBestin the background) to ~2% per hour on my device.

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

回答by samthui7

I did write an app using Location services, app must send location every 10s. And it worked very well.

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

Just use the "allowDeferredLocationUpdatesUntilTraveled:timeout" method, following Apple's doc.

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

Steps are as follows:

步骤如下:

Required:Register background mode for update Location.

要求:为更新位置注册后台模式。

1.Create LocationManger and startUpdatingLocation, with accuracy and filteredDistance as whatever you want:

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.To keep app run forever using "allowDeferredLocationUpdatesUntilTraveled:timeout" method in background, you must restart updatingLocation with new parameter when app moves to background, like this:

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.App gets updatedLocations as normal with "locationManager:didUpdateLocations:" callback:

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.But you should handle the data in then "locationManager:didFinishDeferredUpdatesWithError:" callback for your purpose

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

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

     _deferringUpdates = NO;

     //do something 
}

5.NOTE:I think we should reset parameters of LocationManager each time app switches between background/forground mode.

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

回答by progrmr

  1. If you have the UIBackgroundModesin your plist with locationkey then you don't need to use beginBackgroundTaskWithExpirationHandlermethod. That's redundant. Also you're using it incorrectly (see here) but that's moot since your plist is set.

  2. With UIBackgroundModes locationin the plist the app will continue to run in the background indefinitely only as long as CLLocationMangeris running. If you call stopUpdatingLocationwhile in the background then the app will stop and won't start again.

    Maybe you could call beginBackgroundTaskWithExpirationHandlerjust before calling stopUpdatingLocationand then after calling startUpdatingLocationyou could call the endBackgroundTaskto keep it backgrounded while the GPS is stopped, but I've never tried that - it's just an idea.

    Another option (which I haven't tried) is to keep the location manager running while in the background but once you get an accurate location change the desiredAccuracyproperty to 1000m or higher to allow the GPS chip to get turned off (to save battery). Then 10 minutes later when you need another location update, change the desiredAccuracyback to 100m to turn on the GPS until you get an accurate location, repeat.

  3. When you call startUpdatingLocationon the location manager you must give it time to get a position. You should not immediately call stopUpdatingLocation. We let it run for a maximum of 10 seconds or until we get a non-cached high accuracy location.

  4. You need to filter out cached locations and check the accuracy of the location you get to make sure it meets your minimum required accuracy (see here). The first update you get may be 10 mins or 10 days old. The first accuracy you get may be 3000m.

  5. Consider using the significant location change APIs instead. Once you get the significant change notification, you could start CLLocationManagerfor a few seconds to get a high accuracy position. I'm not certain, I've never used the significant location change services.

  1. 如果你UIBackgroundModes的 plist 中有locationkey ,那么你不需要使用beginBackgroundTaskWithExpirationHandler方法。那是多余的。此外,您使用它的方式不正确(请参阅此处),但由于您的 plist 已设置,因此没有实际意义。

  2. 随着UIBackgroundModes location在plist中的应用程序将继续无限期只只要在后台运行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几秒钟以获得高精度位置。我不确定,我从未使用过重要的位置更改服务。

回答by sash

When it is time to start location service and stop background task, background task should be stopped with a delay (1 second should be enough). Otherwise location service wont start. Also Location Service should be left ON for a couple of seconds (e.g. 3 seconds).

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

There is a cocoapod APScheduledLocationManagerthat allows to get background location updates every nseconds with desired location accuracy.

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

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

The repository also contains an example app written in Swift 3.

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

回答by thatzprem

How about giving it a try with startMonitoringSignificantLocationChanges:API? It definitely fires with less frequency and the accuracy is reasonably good. Additionally it has lot more advantages than using other locationManagerAPI's.

试试startMonitoringSignificantLocationChanges:API 怎么样?它肯定以较低的频率发射并且准确性相当好。此外,它比使用其他locationManagerAPI具有更多优势。

Lot more regarding this API has already been discussed on this link

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

回答by aahsanali

You need to add the location update mode in your application by adding following key in you info.plist.

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

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

didUpdateToLocationmethod will be called (even when your app is in background). You can perform any thing on the bases of this method call

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

回答by Mr.Javed Multani

To use standard location services while the application is in the background you need first turn on Background Modes in the Capabilities tab of the Target settings, and select Location updates.

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

Or, add it directly to the Info.plist.

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

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

Then you need to setup the CLLocationManager

然后你需要设置 CLLocationManager

Objective C

目标 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];

Swift

迅速

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

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

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

回答by Tech

After iOS 8 their are several changes in CoreLocation framework related background fetch and updations made by apple.

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

1) Apple introduce the AlwaysAuthorization request for fetching the location update in the application.

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

2) Apple introduce backgroundLocationupdates for fetching location from background in iOS.

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

For fetching location in background you need to enable Location Update in Capabilities of Xcode.

要在后台获取位置,您需要在 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
    }

}

回答by Papon Smc

locationManager.allowsBackgroundLocationUpdates = true