私は、高精度かつ低頻度でバックグラウンドの位置更新を必要とするアプリケーションを書いています。ソリューションは、ロケーションマネージャの更新を開始し、その後すぐにシャットダウンするバックグラウンドNSTimerタスクのようです。この質問は以前に尋ねられました:
iOSアプリケーションでn分ごとにバックグラウンドで位置情報を更新するにはどうすればよいですか?
アプリがバックグラウンドになってからn分ごとにユーザーの位置を取得する
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秒からゼロに減少し(ロケーションのロギング中)、その後、期限切れハンドラーが実行され、それ以上ロケーションの更新は生成されません。 バックグラウンドで定期的にロケーションの更新を無期限に受信するために上記のコードを変更するにはどうすればよいですか?
Update:iOS 7をターゲットにしていますが、バックグラウンドタスクの動作が異なることを示すいくつかの証拠があるようです:
stopUpdatingLocation
がバックグラウンドウォッチドッグタイマーをトリガーするようですので、didUpdateLocation
を次のように置き換えました:
[self.locationManager setDesiredAccuracy:kCLLocationAccuracyThreeKilometers];
[self.locationManager setDistanceFilter:99999];
これにより、GPSの電源が効果的にオフになります。バックグラウンドNSTimer
のセレクターは次のようになります。
- (void) changeAccuracy {
[self.locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
[self.locationManager setDistanceFilter:kCLDistanceFilterNone];
}
定期的に精度を切り替えて、数分ごとに高精度の座標を取得します。locationManager
が停止していないため、backgroundTimeRemaining
は最大値のままです。これにより、バッテリー消費量が1時間あたり約10%(バックグラウンドでkCLLocationAccuracyBest
が一定)から、デバイスで1時間あたり約2%に削減されました。
ロケーションサービスを使用してアプリを作成しました。アプリは10秒ごとに場所を送信する必要があります。そして、それは非常にうまくいきました。
Appleのドキュメントに従って、「 allowDeferredLocationUpdatesUntilTraveled:timeout 」メソッドを使用するだけです。
手順は次のとおりです。
必須:ロケーションの更新のためにバックグラウンドモードを登録します。
1。LocationMangerとstartUpdatingLocationを作成し、必要に応じて精度とfiltersDistanceを設定します。
-(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:」コールバックを使用して、通常どおり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。ただし、目的に合わせて「locationManager:didFinishDeferredUpdatesWithError:」コールバックでデータを処理する必要があります
- (void) locationManager:(CLLocationManager *)manager didFinishDeferredUpdatesWithError:(NSError *)error {
_deferringUpdates = NO;
//do something
}
5。注:アプリがバックグラウンド/フォアグラウンドモードを切り替えるたびに、LocationManagerのパラメーターをリセットする必要があると思います。
PlistにUIBackgroundModes
キーを持つlocation
がある場合、beginBackgroundTaskWithExpirationHandler
メソッドを使用する必要はありません。それは冗長です。また、それを誤って使用しています( here を参照)が、plistが設定されているため、それは意味がありません。
PlistにUIBackgroundModes location
を指定すると、CLLocationManger
が実行されている限り、アプリはバックグラウンドで無期限に実行され続けます。バックグラウンドでstopUpdatingLocation
を呼び出すと、アプリは停止し、再起動しません。
beginBackgroundTaskWithExpirationHandler
を呼び出す直前にstopUpdatingLocation
を呼び出し、startUpdatingLocation
を呼び出した後、endBackgroundTask
を呼び出して、GPSが停止している間バックグラウンドを維持することができますが、それを試した-それは単なるアイデアです。
別のオプション(私は試していません)は、バックグラウンドでロケーションマネージャーを実行し続けることですが、正確なロケーションを取得したら、desiredAccuracy
プロパティを1000m以上に変更してGPSチップをオフにできます(バッテリーを節約するため)。その後、10分後に別の位置の更新が必要になったら、desiredAccuracy
を100mに戻し、正確な位置が得られるまでGPSをオンにして、繰り返します。
ロケーションマネージャでstartUpdatingLocation
を呼び出すとき、位置を取得する時間を与える必要があります。すぐにstopUpdatingLocation
を呼び出さないでください。最大10秒間、またはキャッシュされていない高精度の位置が取得されるまで実行します。
キャッシュされた場所を除外し、取得した場所の精度をチェックして、必要な最小精度を満たしていることを確認する必要があります( here を参照)。最初に取得する更新は、10分または10日前です。最初に得られる精度は3000mです。
代わりに重要な場所変更APIの使用を検討してください。重要な変更通知を受け取ったら、CLLocationManager
を数秒間起動して、高精度の位置を取得できます。確かなことではないが、重要なロケーション変更サービスを使用したことがない。
位置情報サービスを開始し、バックグラウンドタスクを停止する時間になったら、バックグラウンドタスクを遅らせて停止する必要があります(1秒で十分です)。そうでない場合、位置情報サービスは開始されません。また、位置情報サービスは数秒間(3秒など)オンにしておく必要があります。
Cocoapod APScheduledLocationManager があります。これにより、目的の位置精度でn秒ごとにバックグラウンドの位置更新を取得できます。
let manager = APScheduledLocationManager(delegate: self)
manager.startUpdatingLocation(interval: 170, acceptableLocationAccuracy: 100)
リポジトリには、Swift 3で記述されたサンプルアプリも含まれています。
startMonitoringSignificantLocationChanges:
APIで試してみませんか?確実に少ない頻度で発火し、精度はかなり良いです。さらに、他のlocationManager
APIを使用するよりも多くの利点があります。
このAPIに関する詳細は、これについて既に説明しました link
Info.plistに次のキーを追加して、アプリケーションに位置更新モードを追加する必要があります。
<key>UIBackgroundModes</key>
<array>
<string>location</string>
</array>
didUpdateToLocation
メソッドが呼び出されます(アプリがバックグラウンドにある場合でも)。このメソッド呼び出しのベースで何でも実行できます
IOS 8以降、CoreLocationフレームワークに関連するバックグラウンドフェッチとAppleによる更新がいくつか変更されています。
1)Appleは、アプリケーションでロケーションの更新を取得するためのAlwaysAuthorizationリクエストを導入します。
2)Appleは、iOSの背景から場所を取得するためのbackgroundLocationupdatesを導入します。
バックグラウンドで場所を取得するには、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
}
}
アプリケーションがバックグラウンドにあるときに標準の位置情報サービスを使用するには、最初に[ターゲット]設定の[機能]タブで[バックグラウンドモード]をオンにし、[位置情報の更新]を選択する必要があります。
または、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];
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()
参照: https://medium.com/@javedmultani16/location-service-in-the-background-ios-942c89cd99ba