[ios] 주기적인 iOS 백그라운드 위치 업데이트

높은 정확도와 낮은 빈도로 백그라운드 위치 업데이트가 필요한 응용 프로그램을 작성 중 입니다. 솔루션은 위치 관리자의 업데이트를 시작하고 즉시 종료되는 백그라운드 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 초에서 0 (위치 로깅 중)으로 감소한 다음 만료 처리기가 실행되고 추가 위치 업데이트가 생성되지 않는 것입니다. 백그라운드에서주기적인 위치 업데이트를 무기한 수신하려면 위 코드를 어떻게 수정 합니까?

업데이트 : iOS 7을 대상으로하고 있으며 백그라운드 작업이 다르게 작동한다는 몇 가지 증거가있는 것 같습니다.

백그라운드 작업에서 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최대 값을 유지합니다. 이로 인해 배터리 소모량이 시간당 ~ 10 % ( kCLLocationAccuracyBest백그라운드에서 일정하게 유지됨)에서 내 장치에서 시간당 ~ 2 %로 감소했습니다.


답변

위치 서비스를 사용하여 앱을 작성했는데 앱은 10 초마다 위치를 전송해야합니다. 그리고 그것은 아주 잘 작동했습니다.

Apple의 문서에 따라 ” allowDeferredLocationUpdatesUntilTraveled : timeout “메소드를 사용하십시오 .

단계는 다음과 같습니다.

필수 : 업데이트 위치에 대한 백그라운드 모드를 등록합니다.

1. 원하는대로 정확도와 filterDistance를 사용하여 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. 백그라운드에서 “allowDeferredLocationUpdatesUntilTraveled : timeout”메소드를 사용하여 앱을 계속 실행하려면 다음과 같이 앱이 백그라운드로 이동할 때 새 매개 변수로 updatesLocation을 다시 시작해야합니다.

- (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의 매개 변수를 재설정해야한다고 생각합니다.


답변

  1. 당신이있는 경우 UIBackgroundModes에 당신의 plist에 location키 당신은 사용에 필요하지 않은 beginBackgroundTaskWithExpirationHandler방법. 그것은 중복입니다. 또한 잘못 사용하고 있지만 ( 여기 참조 ) plist가 설정 되었기 때문에 문제가됩니다.

  2. 함께 UIBackgroundModes locationPLIST의 응용 프로그램은 무기한 동안 만 같이 백그라운드에서 계속 실행됩니다 CLLocationManger실행됩니다. stopUpdatingLocation백그라운드에서 전화 하면 앱이 중지되고 다시 시작되지 않습니다.

    전화 beginBackgroundTaskWithExpirationHandler하기 직전에 전화를 걸고 전화 stopUpdatingLocation를 한 후 GPS가 중지 된 동안 백그라운드 상태를 유지하기 위해에게 전화를 startUpdatingLocation걸 수도 endBackgroundTask있지만, 시도해 본 적이 없습니다.

    또 다른 옵션 (내가 시도하지 않은)은 백그라운드에있는 동안 위치 관리자를 계속 실행하는 것입니다.하지만 정확한 위치를 얻으면 desiredAccuracy속성을 1000m 이상으로 변경 하여 GPS 칩이 꺼 지도록합니다 (배터리 절약을 위해). 그런 다음 10 분 후 다른 위치 업데이트가 필요하면 desiredAccuracy다시 100m로 변경하여 정확한 위치를 얻을 때까지 GPS를 켜고 반복합니다.

  3. startUpdatingLocation위치 관리자 를 호출 할 때 위치를 확보 할 시간을 주어야합니다. 즉시 전화해서는 안됩니다 stopUpdatingLocation. 최대 10 초 동안 또는 캐시되지 않은 고정밀 위치를 얻을 때까지 실행되도록합니다.

  4. 캐시 된 위치를 필터링하고 필요한 최소 정확도를 충족하는지 확인하는 위치의 정확도를 확인해야합니다 ( 여기 참조 ). 첫 번째 업데이트는 10 분 또는 10 일이 지난 것입니다. 첫 번째 정확도는 3000m 일 수 있습니다.

  5. 대신 중요한 위치 변경 API를 사용해보십시오. 중요한 변경 알림을 받으면 CLLocationManager몇 초 동안 시작 하여 정확한 위치를 얻을 수 있습니다. 확실하지 않습니다. 중요한 위치 변경 서비스를 사용한 적이 없습니다.


답변

위치 서비스를 시작하고 백그라운드 작업을 중지 할 때가되면 백그라운드 작업이 지연되고 중지되어야합니다 (1 초이면 충분 함). 그렇지 않으면 위치 서비스가 시작되지 않습니다. 또한 위치 서비스는 몇 초 (예 : 3 초) 동안 켜져 있어야합니다.

원하는 위치 정확도로 n 초 마다 백그라운드 위치 업데이트를받을 수 있는 cocoapod APScheduledLocationManager 가 있습니다 .

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

저장소에는 Swift 3로 작성된 예제 앱도 포함되어 있습니다.


답변

startMonitoringSignificantLocationChanges:API를 사용해 보는 것은 어떻습니까? 확실히 적은 빈도로 발사되며 정확도는 상당히 좋습니다. 또한 다른 locationManagerAPI를 사용하는 것보다 훨씬 더 많은 이점이 있습니다.

이 API에 대한 더 많은 내용은 이미이 링크 에서 논의되었습니다.


답변

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
    }

}