我正在寻找一种在我的 iOS 应用程序中每 n 分钟更新一次后台位置的方法。我使用的是 iOS 4.3,该解决方案应该适用于未越狱的 iPhone。
我尝试/考虑了以下选项:
-
CLLocationManager startUpdatingLocation/startMonitoringSignificantLocationChanges
:这根据配置的属性按预期在后台工作,但似乎不可能强制它每 n 分钟更新一次位置
NSTimer
:当应用程序在前台运行时可以工作,但似乎不是为后台任务设计的
- 本地通知:可以每n分钟安排一次本地通知,但不可能执行一些代码来获取当前位置(用户无需通过通知启动应用程序)。这种方法似乎也不是一种干净的方法,因为这不是通知应该使用的用途。
-
UIApplication:beginBackgroundTaskWithExpirationHandler
:据我了解,当应用程序移至后台时,这应该用于在后台完成一些工作(也受时间限制),而不是实现“长时间运行”后台进程。
如何实现这些定期后台位置更新?
I'm looking for a way to get a background location update every n minutes in my iOS application. I'm using iOS 4.3 and the solution should work for non-jailbroken iPhones.
I tried / considered following options:
CLLocationManager startUpdatingLocation/startMonitoringSignificantLocationChanges
: This works in the background as expected, based on the configured properties, but it seems not possible to force it to update the location every n minutes
NSTimer
: Does work when the app is running in the foreground but doesn't seem to be designed for background tasks
- Local notifications: Local notifications can be scheduled every n minutes, but it's not possible to execute some code to get the current location (without the user having to launch the app via the notification). This approach also doesn't seem to be a clean approach as this is not what notifications should be used for.
UIApplication:beginBackgroundTaskWithExpirationHandler
: As far as I understand, this should be used to finish some work in the background (also limited in time) when an app is moved to the background rather than implementing "long-running" background processes.
How can I implement these regular background location updates?
发布评论
评论(14)
我在Apple开发者论坛的帮助下找到了实现此目的的解决方案:
位置后台模式
UIApplication:beginBackgroundTaskWithExpirationHandler:在后台创建一个
NSTimer
:n
小于UIApplication:backgroundTimeRemaining
时,它会正常工作。当n
更大时,应在没有剩余时间之前再次启用(和禁用)位置管理器
,以避免后台任务被终止。这是可行的,因为位置是三种允许的后台执行类型之一。
注意:我在模拟器中测试它,但它不起作用,因此浪费了一些时间。不过,它在我的手机上运行良好。
I found a solution to implement this with the help of the Apple Developer Forums:
location background mode
NSTimer
in the background withUIApplication:beginBackgroundTaskWithExpirationHandler:
n
is smaller thanUIApplication:backgroundTimeRemaining
it will work just fine. Whenn
is larger, thelocation manager
should be enabled (and disabled) again before there is no time remaining to avoid the background task being killed.This works because location is one of the three allowed types of background execution.
Note: I lost some time by testing this in the simulator where it doesn't work. However, it works fine on my phone.
在 iOS 8/9/10 上,要每 5 分钟更新一次后台位置,请执行以下操作:
转到项目 ->能力->背景模式 ->选择位置更新
转到项目 ->信息->添加一个具有空值(或可选的任何文本)的键 NSLocationAlwaysUsageDescription
要使位置在您的应用程序处于后台时工作并将坐标发送到 Web 服务或每 5 分钟对它们执行任何操作,请像下面的代码一样实现它。< /p>
我没有使用任何后台任务或计时器。我已经用我的 iOS 8.1 设备测试了这段代码,该设备放在我的桌子上几个小时,我的应用程序在后台运行。设备被锁定,代码始终正常运行。
On iOS 8/9/10 to make background location update every 5 minutes do the following:
Go to Project -> Capabilities -> Background Modes -> select Location updates
Go to Project -> Info -> add a key NSLocationAlwaysUsageDescription with empty value (or optionally any text)
To make location working when your app is in the background and send coordinates to web service or do anything with them every 5 minutes implement it like in the code below.
I'm not using any background tasks or timers. I've tested this code with my device with iOS 8.1 which was lying on my desk for few hours with my app running in the background. Device was locked and the code was running properly all the time.
我在我正在开发的应用程序中这样做了。当应用程序处于后台但应用程序不断接收位置更新时,计时器不起作用。我在文档中的某个地方读到(我现在似乎找不到它,我会在找到时发布更新),当应用程序位于后台时,只能在活动运行循环上调用方法。即使在背景中,应用程序委托也有一个活动的运行循环,因此您不需要创建自己的运行循环来使其工作。
[我不确定这是否是正确的解释,但这就是我从所读内容中的理解]
首先,在应用程序信息中添加键
UIBackgroundModes
的location
对象.plist。现在,您需要做的是在应用程序中的任何位置启动位置更新:接下来,编写一个方法来处理位置更新,
在应用程序委托中说
-(void)didUpdateToLocation:(CLLocation*)location
。然后在启动位置管理器的类中实现 CLLocationManagerDelegate 的locationManager:didUpdateLocation:fromLocation
方法(因为我们将位置管理器委托设置为“self”)。在此方法中,您需要检查必须处理位置更新的时间间隔是否已过。您可以通过每次保存当前时间来做到这一点。如果该时间已过,请从应用程序委托中调用 UpdateLocation 方法:即使您的应用程序处于后台,这也会每 5 分钟调用您的方法一次。
Imp:此实现会耗尽电池,如果您的位置数据的准确性并不重要,您应该使用
[locationManager startMonitoringSignificantLocationChanges]
在将此添加到您的应用程序之前,请阅读 位置感知编程指南
I did this in an application I'm developing. The timers don't work when the app is in the background but the app is constantly receiving the location updates. I read somewhere in the documentation (i can't seem to find it now, i'll post an update when i do) that a method can be called only on an active run loop when the app is in the background. The app delegate has an active run loop even in the bg so you dont need to create your own to make this work.
[Im not sure if this is the correct explanation but thats how I understood from what i read]
First of all, add the
location
object for the keyUIBackgroundModes
in your app's info.plist. Now, what you need to do is start the location updates anywhere in your app:Next, write a method to handle the location updates,
say
-(void)didUpdateToLocation:(CLLocation*)location
, in the app delegate. Then implement the methodlocationManager:didUpdateLocation:fromLocation
ofCLLocationManagerDelegate
in the class in which you started the location manager (since we set the location manager delegate to 'self'). Inside this method you need to check if the time interval after which you have to handle the location updates has elapsed. You can do this by saving the current time every time. If that time has elapsed, call the method UpdateLocation from your app delegate:This will call your method every 5 mins even when your app is in background.
Imp: This implementation drains the battery, if your location data's accuracy is not critical you should use
[locationManager startMonitoringSignificantLocationChanges]
Before adding this to your app, please read the Location Awareness Programming Guide
既然 iOS6 已经推出,那么拥有永久运行的位置服务的最佳方式是......
只是这样测试:
我启动了应用程序,进入后台并在车内移动了几分钟。然后我回家 1 小时并再次开始移动(无需再次打开应用程序)。地点又开始了。然后停了两个小时又开始了。一切又恢复正常了...
不要忘记使用 iOS6 中的新位置服务
Now that iOS6 is out the best way to have a forever running location services is...
Just tested it like that:
I started the app, go background and move in the car by some minutes. Then I go home for 1 hour and start moving again (without opening again the app). Locations started again. Then stopped for two hours and started again. Everything ok again...
DO NOT FORGET USING the new location services in iOS6
对于其他有噩梦的人来说,请弄清楚这个。我有一个简单的解决方案。
使用以下命令添加计时器:
只是不要忘记在 info.plist 中添加“应用程序注册位置更新”。
To someone else having nightmare figure out this one. I have a simple solution.
Add timer by using :
Just don't forget to add "App registers for location updates" in info.plist.
这是我使用的:
我在 AppDelegate 中开始跟踪,如下所示:
Here is what I use:
I start the tracking in AppDelegate like that:
不幸的是,您的所有假设似乎都是正确的,并且我认为没有办法做到这一点。为了节省电池寿命,iPhone的定位服务是基于移动的。如果手机放在一个地方,定位服务就看不到它。
当手机收到位置更新时,
CLLocationManager
只会调用locationManager:didUpdateToLocation:fromLocation:
,只有在三种位置服务(手机信号塔、gps、wifi)之一时才会发生这种情况) 感知到变化。其他一些可能有助于提供进一步解决方案的事情:
启动和启动 服务会导致调用
didUpdateToLocation
委托方法,但newLocation
可能具有旧时间戳。区域监控可能会有所帮助
在后台运行时,请注意,可能很难获得“完整”的 LocationServices 支持批准由苹果公司。据我所知,他们专门设计了
startMonitoringSignificantLocationChanges
作为需要后台位置支持的应用程序的低功耗替代方案,并强烈鼓励开发人员使用它,除非应用程序绝对需要它。祝你好运!
更新:这些想法现在可能已经过时了。看起来人们似乎通过上面@wjans 的回答取得了成功。
Unfortunately, all of your assumptions seem correct, and I don't think there's a way to do this. In order to save battery life, the iPhone's location services are based on movement. If the phone sits in one spot, it's invisible to location services.
The
CLLocationManager
will only calllocationManager:didUpdateToLocation:fromLocation:
when the phone receives a location update, which only happens if one of the three location services (cell tower, gps, wifi) perceives a change.A few other things that might help inform further solutions:
Starting & Stopping the services causes the
didUpdateToLocation
delegate method to be called, but thenewLocation
might have an old timestamp.Region Monitoring might help
When running in the background, be aware that it may be difficult to get "full" LocationServices support approved by Apple. From what I've seen, they've specifically designed
startMonitoringSignificantLocationChanges
as a low power alternative for apps that need background location support, and strongly encourage developers to use this unless the app absolutely needs it.Good Luck!
UPDATE: These thoughts may be out of date by now. Looks as though people are having success with @wjans answer, above.
我确实使用位置服务编写了一个应用程序,应用程序必须每 10 秒发送一次位置。
而且效果非常好。
只需使用“allowDeferredLocationUpdatesUntilTraveled:timeout”方法,遵循Apple的文档。
我所做的是:
必需:注册后台模式以更新位置。
1. 创建
LocationManger
和startUpdatingLocation
,将accuracy
和filteredDistance
设置为您想要的值:2. 要在后台使用
allowDeferredLocationUpdatesUntilTraveled:timeout
方法让应用程序永远运行,您必须在以下情况下使用新参数重新启动updatingLocation
应用程序移动到后台,如下所示:3. 应用程序通过
locationManager:didUpdateLocations:
回调正常获取更新位置:4. 但您应该处理然后
locationManager:didFinishDeferredUpdatesWithError:
回调中的数据5. 注意: 我认为我们应该重置以下参数每次应用程序在后台/前台模式之间切换时,
LocationManager
。I did write an app using Location services, app must send location every 10s.
And it worked very well.
Just use the "allowDeferredLocationUpdatesUntilTraveled:timeout" method, following Apple's doc.
What I did are:
Required: Register background mode for update Location.
1. Create
LocationManger
andstartUpdatingLocation
, withaccuracy
andfilteredDistance
as whatever you want:2. To keep app run forever using
allowDeferredLocationUpdatesUntilTraveled:timeout
method in background, you must restartupdatingLocation
with new parameter when app moves to background, like this:3. App gets updatedLocations as normal with
locationManager:didUpdateLocations:
callback:4. But you should handle the data in then
locationManager:didFinishDeferredUpdatesWithError:
callback for your purpose5. NOTE: I think we should reset parameters of
LocationManager
each time app switches between background/forground mode.从 iOS 9 开始,这是后台位置跟踪所必需的。
This is needed for background location tracking since iOS 9.
我使用了 xs2bush 的获取间隔的方法(使用 timeIntervalSinceDate)并对其进行了一些扩展。我想确保我获得了所需的精度,并且确保我不会因为过度使用 GPS 无线电而耗尽电池电量。
我使用以下设置保持位置连续运行:
这是电池消耗相对较低的情况。当我准备好获取下一个定期位置读数时,我首先检查该位置是否在我想要的精度范围内,如果是,我然后使用该位置。如果不是,那么我会通过以下方式提高精度:
获取我的位置,然后一旦获得位置,我就会再次降低精度,以最大程度地减少电池的消耗。我已经编写了一个完整的工作示例,并且还编写了服务器端代码的源代码来收集位置数据,将其存储到数据库并允许用户实时查看 GPS 数据或检索和查看以前存储的路线。我有 iOS、Android、Windows Phone 和 Java Me 客户端。所有客户端都是原生编写的,并且它们都在后台正常工作。该项目已获得麻省理工学院许可。
iOS 项目使用 iOS 7 的基础 SDK 面向 iOS 6。您可以获取代码
如果您发现任何问题,请在 github 上提交问题。谢谢。
I used xs2bush's method of getting an interval (using
timeIntervalSinceDate
) and expanded on it a little bit. I wanted to make sure that I was getting the required accuracy that I needed and also that I was not running down the battery by keeping the gps radio on more than necessary.I keep location running continuously with the following settings:
this is a relatively low drain on the battery. When I'm ready to get my next periodic location reading, I first check to see if the location is within my desired accuracy, if it is, I then use the location. If it's not, then I increase the accuracy with this:
get my location and then once I have the location I turn the accuracy back down again to minimize the drain on the battery. I have written a full working sample of this and also I have written the source for the server side code to collect the location data, store it to a database and allow users to view gps data in real time or retrieve and view previously stored routes. I have clients for iOS, android, windows phone and java me. All clients are natively written and they all work properly in the background. The project is MIT licensed.
The iOS project is targeted for iOS 6 using a base SDK of iOS 7. You can get the code here.
Please file an issue on github if you see any problems with it. Thanks.
似乎 stopUpdatingLocation 是触发后台看门狗计时器的原因,因此我在 didUpdateLocation 中将其替换为:
这似乎有效地关闭了 GPS 电源。然后,后台 NSTimer 的选择器变为:
我所做的就是每隔几分钟定期切换精度以获取高精度坐标,并且由于 locationManager 尚未停止,backgroundTimeRemaining 保持其最大值。在我的设备上,这将电池消耗从每小时约 10%(后台 kCLLocationAccuracyBest 恒定)减少到每小时约 2%
It seems that stopUpdatingLocation is what triggers the background watchdog timer, so I replaced it in didUpdateLocation with:
which appears to effectively power down the GPS. The selector for the background NSTimer then becomes:
All I'm doing is periodically toggling the accuracy to get a high-accuracy coordinate every few minutes and because the locationManager hasn't been stopped, backgroundTimeRemaining stays at its maximum value. This reduced battery consumption from ~10% per hour (with constant kCLLocationAccuracyBest in the background) to ~2% per hour on my device
有一个 cocoapod APScheduledLocationManager 允许每 n 秒获取后台位置更新所需的定位精度。
该存储库还包含一个用 Swift 3 编写的示例应用程序。
There is a cocoapod APScheduledLocationManager that allows to get background location updates every n seconds with desired location accuracy.
The repository also contains an example app written in Swift 3.
在 iOS 9 和 watchOS 2.0 中,CLLocationManager 上有一个新方法可以让您请求当前位置:CLLocationManager:requestLocation()。这会立即完成,然后将位置返回给 CLLocationManager 委托。
您现在可以使用 NSTimer 通过此方法每分钟请求一个位置,而不必使用 startUpdatingLocation 和 stopUpdatingLocation 方法。
但是,如果您想根据距上次位置 X 米的变化来捕获位置,只需设置 CLLocationManger 的 distanceFilter 属性并对 X 调用 startUpdatingLocation() 即可。
In iOS 9 and watchOS 2.0 there's a new method on CLLocationManager that lets you request the current location: CLLocationManager:requestLocation(). This completes immediately and then returns the location to the CLLocationManager delegate.
You can use an NSTimer to request a location every minute with this method now and don't have to work with startUpdatingLocation and stopUpdatingLocation methods.
However if you want to capture locations based on a change of X meters from the last location, just set the distanceFilter property of CLLocationManger and to X call startUpdatingLocation().
附件是一个基于以下内容的 Swift 解决方案:
在 info.plist 中定义
App 注册位置更新
保持 locationManager 始终运行
在
BestForNavigation
之间切换kCLLocationAccuracy
>(等待 5 秒获取位置)并在剩余的等待时间内使用ThreeKilometers
以避免电池耗尽此示例在前台每 1 分钟更新一次位置,每 15 秒更新一次位置分钟在后台。
该示例适用于在 iOS 7 设备中运行的 Xcode 6 Beta 6。
在App Delegate中(mapView是一个Optional,指向mapView Controller)
在mapView中(locationManager指向AppDelegate中的对象)
Attached is a Swift solution based in:
Define
App registers for location updates
in the info.plistKeep the locationManager running all the time
Switch
kCLLocationAccuracy
betweenBestForNavigation
(for 5 secs to get the location) andThreeKilometers
for the rest of the wait period to avoid battery drainageThis example updates location every 1 min in Foreground and every 15 mins in Background.
The example works fine with Xcode 6 Beta 6, running in a iOS 7 device.
In the App Delegate (mapView is an Optional pointing to the mapView Controller)
In the mapView (locationManager points to the object in the AppDelegate)