定期的なiOSバックグラウンドの場所の更新


91

高精度かつ低頻度でバックグラウンドでの位置情報の更新を必要とするアプリケーションを書いています。解決策は、Location Managerの更新を開始してすぐにシャットダウンするバックグラウンド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];

}

現在の動作は、backgroundTimeRemaining180秒からゼロにデクリメント(場所のログ記録中)してから、期限切れハンドラーが実行され、それ以上の場所の更新は生成されません。バックグラウンドで定期的な位置情報の更新を無期限に受け取るために、上記のコードを変更するにはどうすればよいですか?

更新:私はiOS 7をターゲットにしており、バックグラウンドタスクの動作が異なるといういくつかの証拠があります:

バックグラウンドタスクからiOS 7でロケーションマネージャーを起動する


対象としているiOSのバージョンについては言及していません。iOS 7(たとえば)は、以前のものとは異なるマルチタスクシステムを備えています。
Jason Whitehorn 2013

@JasonWhitehorn良い点。質問を更新しました。
2013

2
@pcoving:アプリが強制終了または終了されても、ソリューションは機能しますか?
Nirav、2016年

回答:


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最大値のままです。これにより、バッテリー消費量が1時間あたり最大10%(kCLLocationAccuracyBestバックグラウンドでは常に)から、私のデバイスでは1時間あたり最大2%に削減されました。


4
完全に機能する例で質問を更新できますか?「更新:わかった。これが私の解決策です...」
smholloway 2013年

@Virussmca私は以前の回答に戻しましたが、同様のパフォーマンスではるかに堅牢です。位置情報の更新を完全に停止すると、競合状態(またはその他の非決定的なシナリオ)がトリガーされるようです。
2013年

このアプローチは期待どおりに機能しません。1)gpsは現在地を把握し続ける必要があるため、distanceFilterプロパティの値を増やしても、バッテリーは節約されません。2)iOS 7では、バックグラウンド時間が連続していません。3)重要な変更場所サービスを考慮に入れる場合があります。4)iOS 7のsoutionを除いて、UIBackgroundModesを開始できません-背景からの位置...
aMother

2
@aMother 1)距離フィルターを増やす(コールバックを処理しない)と、バッテリーを節約できないことを理解しています。ただし、setDesiredAccuracyにはあります。それは基本的に無料で提供されるセルタワー情報にフォールバックします。2)隣接していない?3)高精度が必要であることを太字で指定しました。重要な場所の変更はこれを提供しません。4)位置情報サービスがバックグラウンドから開始/停止されていません。
2013年

2
私はこれが古い投稿であることを知っていますが、このトリックを使用する私のアプリケーションのユーザーは、iOS9にアップグレードしてからバッテリー使用量が大幅に増加していることに気づきました。まだ調査はしていませんが、この「トリック」が機能しなくなった気がします。
Gary Wright、

45

位置情報サービスを使用してアプリを作成しましたが、アプリは10秒ごとに位置情報を送信する必要があります。そして、それは非常にうまくいきました。

Appleのドキュメントに従って、「allowDeferredLocationUpdatesUntilTraveled:timeout」メソッドを使用するだけです。

手順は次のとおりです。

必須:更新場所のバックグラウンドモードを登録します。

1. LocationMangerとstartUpdatingLocationを作成します精度は任意で、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」メソッドを使用してアプリを永久に実行し続けるには、次のように、アプリがバックグラウンドに移動したときに、新しいパラメーターで更新場所を再起動する必要があります。

- (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のパラメーターをリセットする必要があると思います。


これはうまく機能し、昨年のWWDCで言及されているように、これは正しい方法だと思います!! 投稿ありがとう
prasad1250 '11 / 11/25

@ samthui7はこの作業に感謝しますが、終了後も位置を取得する必要があります。どうすればいいの?。?
Mohit Tomar

@ samthui7 ..このQUE ...参照してください。stackoverflow.com/questions/37338466/...
スラジSukale

2
@amar:私はiOS 10ベータ版もチェックしましたが、それでも動作します。startUpdatingLocation:allowsBackgroundLocationUpdates = YESおよびrequestAlwaysAuthorizationまたはの前にコードを追加する必要があることに注意してくださいrequestWhenInUseAuthorization。例: `CLLocationManager * locationManager = [[CLLocationManager alloc] init]; locationManager.delegate = self; locationManager.allowsBackgroundLocationUpdates = YES; [locationManager requestAlwaysAuthorization]; [locationManager startUpdatingLocation]; `
samthui7 '20

1
@Niravいいえ。私が知る限り、Appleがそれを許可することはできないと思います。
samthui7 2016年

38
  1. キーUIBackgroundModes付きのplistにがある場合は、メソッドlocationを使用beginBackgroundTaskWithExpirationHandlerする必要はありません。それは冗長です。また、あなたはそれを間違って使用していますが(ここを参照)、plistが設定されているので、それは意味がありません。

  2. UIBackgroundModes locationplistの中にアプリが無限にのみ限りバックグラウンドで実行し続けますCLLocationManger実行されています。stopUpdatingLocationバックグラウンドで呼び出した場合、アプリは停止し、再び起動しません。

    たぶん、電話をかけるbeginBackgroundTaskWithExpirationHandler直前に電話をかけstopUpdatingLocation、電話startUpdatingLocationをかけた後endBackgroundTask、GPSが停止している間、を呼び出してバックグラウンドを維持することもできますが、私は一度も試したことはありません。

    別のオプション(私は試していません)は、バックグラウンドでロケーションマネージャーを実行し続けることですが、正確なロケーションを取得したら、desiredAccuracyプロパティを1000m以上に変更して、GPSチップをオフにできるようにします(バッテリーを節約するため)。次に、10分後に別の位置情報を更新する必要があるdesiredAccuracy場合は、正確な位置情報が得られるまでGPSをオンにするように100mに戻します。これを繰り返します。

  3. startUpdatingLocationロケーションマネージャーを呼び出すときは、ポジションを取得する時間を与える必要があります。すぐに電話してはいけませんstopUpdatingLocation。最大10秒間、またはキャッシュされていない高精度の位置を取得するまで、実行します。

  4. キャッシュされた場所を除外し、取得した場所の精度をチェックして、最低限必要な精度を満たしていることを確認する必要があります(こちらを参照)。最初に取得する更新は、10分または10日前のものです。あなたが得る最初の精度は3000メートルかもしれません。

  5. 代わりに重要な場所変更APIの使用を検討してください。重要な変更の通知を受け取ったらCLLocationManager、数秒間開始して高精度の位置を取得できます。私は確信がありません、私は重要な位置変更サービスを使用したことがありません。


Apple CoreLocationリファレンスは、beginBackgroundTaskWithExpirationHandler位置データをバックグラウンドで処理する場合の使用を推奨しているようです。「iOSアプリが位置データを処理するためにより多くの時間を必要とする場合、UIApplicationクラスのbeginBackgroundTaskWithName:expirationHandler:メソッドを使用して、より多くのバックグラウンド実行時間を要求できます。」- developer.apple.com/library/ios/documentation/UserExperience/...
eliocs

4
_locationManager.allowsBackgroundLocationUpdates = YES;を追加することを忘れないでください。iOS9以降の場合、それ以外の場合、アプリは180秒後にスリープします
kolyancz

@kolyancz iOS 10.1.1 for iPhone 6+で試してみました。スリープ状態になり、トリガーされるまでに約17分かかりましたlocationManagerDidPauseLocationUpdates。その動作を10回確認しました!
ハニー、

なぜあなたがそれが愚かであると言っているのか、そしてあなたはbackgroundTaskの中にそれをラップするべきだと言っている理由が本当にわかりません...
Honey

9

位置情報サービスを開始してバックグラウンドタスクを停止するときは、バックグラウンドタスクを遅延して停止する必要があります(1秒で十分です)。そうしないと、位置情報サービスが開始されません。また、位置情報サービスを数秒間オンにしておく必要があります(例:3秒)。

cocoapod APScheduledLocationManagerがあり、 バックグラウンドでのロケーション更新をn秒ごとに希望のロケーション精度で取得できます。

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

リポジトリには、Swift 3で記述されたサンプルアプリも含まれています。


1

startMonitoringSignificantLocationChanges:APIを試してみませんか?それは間違いなく少ない頻度で発射し、精度はかなり良いです。さらに、他のものを使用するよりもはるかに多くの利点がありますlocationManager APIます。

このAPIに関する詳細は、このリンクで既に説明されています。


1
私はこのアプローチを試しました。精度要件に違反しています- ドキュメントから: This interface delivers new events only when it detects changes to the device’s associated cell towers
pcoving

1

info.plistに次のキーを追加して、アプリケーションに位置更新モードを追加する必要があります。

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

didUpdateToLocationメソッドが呼び出されます(アプリがバックグラウンドにある場合でも)。このメソッド呼び出しに基づいてあらゆることを実行できます


位置情報更新モードはすでにinfo.plistにあります。前述のように、バックグラウンドタイムアウトが180秒になるまで更新を取得しています。
2013

0

iOS 8以降は、CoreLocationフレームワークに関連するいくつかの変更であり、アップルによるバックグラウンドフェッチと更新に関連しています。

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
    }

}

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


-2
locationManager.allowsBackgroundLocationUpdates = true

4
回答を編集して、なぜ/これによって問題が解決されるのかについて説明を追加していただけませんか
barbsan
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.