İOS uygulamamda her n dakikada bir arka planda konum güncellemesi nasıl alabilirim?


207

İOS uygulamamda her n dakikada bir arka plan konum güncellemesi almanın bir yolunu arıyorum. İOS 4.3 kullanıyorum ve çözüm jailbreak yapılmamış iPhone'lar için çalışmalı.

Aşağıdaki seçenekleri denedim / değerlendirdim:

  • CLLocationManager startUpdatingLocation/startMonitoringSignificantLocationChanges: Bu, yapılandırılmış özelliklere göre arka planda beklendiği gibi çalışır, ancak konumu her n dakikada bir güncellemeye zorlamak mümkün görünmemektedir.
  • NSTimer: Uygulama ön planda çalışırken çalışır, ancak arka plan görevleri için tasarlanmamış gibi görünür
  • Yerel bildirimler: Yerel bildirimler her n dakikada bir programlanabilir, ancak geçerli konumu almak için bazı kodları çalıştırmak mümkün değildir (kullanıcı uygulamayı bildirim yoluyla başlatmak zorunda kalmadan). Bu yaklaşım aynı zamanda temiz bir yaklaşım gibi görünmemektedir, çünkü bildirimlerin kullanılması gereken bu değildir.
  • UIApplication:beginBackgroundTaskWithExpirationHandler: Anladığım kadarıyla, bu, bir uygulama "uzun süren" arka plan işlemleri uygulamak yerine arka plana taşındığında arka planda (zamanla da sınırlı) bazı işleri bitirmek için kullanılmalıdır.

Bu düzenli arka plan konum güncellemelerini nasıl uygulayabilirim?




1
İOS 7'de çalışmasını sağlamaya çalışıyorsanız, bu çözümü burada deneyebilirsiniz: stackoverflow.com/questions/18946881/… Herhangi bir sorunuz varsa, burada bir tartışma için bize katılmanız için bekliyoruz: mobileoop.com/background -location-update-programming-ios-7 için
Ricky

1
Tüm bulgularınız doğrudur (dört madde işareti). Değerli bilgiler o zaman, neyle uyuşmadığını bilmek durumunda? Ve evet, ASMA MODDA veya ÇALIŞMIYORSA, her n-dakikada bir güncelleme için nihai bir yöntem yoktur.
LenArt

Yanıtlar:


113

Apple Geliştirici Forumlarının yardımıyla bunu uygulamak için bir çözüm buldum:

  • belirtmek location background mode
  • İle NSTimerarka planda bir oluşturUIApplication:beginBackgroundTaskWithExpirationHandler:
  • Ne zaman ndaha küçük daha UIApplication:backgroundTimeRemainingiyi çalışır. Ne zaman nolduğunu daha büyük , location manageryine daha önce öldürülme arka plan görevi önlemek için kalan zaman yoktur etkin (engelli) gerekmektedir.

Konum, izin verilen üç arka plan yürütme türünden biri olduğu için çalışır .

Not: Simülatörde çalışmadığı zaman test ederek biraz zaman kaybettim. Ancak, telefonumda iyi çalışıyor.


24
forumun bağlantısı var mı? Aynı tür konum eşleştirmesini uygulamak istiyorum ve çalıştıramadım. Veya bazı örnek kod büyük mutluluk duyacağız.
utahwithak

4
Konum yöneticisini durdurup başlatırsanız, arka plan görevinin neden 10 dakika sonra (izin verilen maksimum süre) öldürülmediğini açıklayabilir misiniz? bir çeşit amaçlanan işlevsellik mi? eğer böyle olursa Apple SDK'daki bir hataya benziyor. Hangi iOS sürümünü deniyorsunuz?
saurabh

5
@all: Evet, uygulamamız AppStore'da mevcuttur. Tüm kodu göndermeyeceğim, bahsedilen tüm bullet noktaları açıkça belgelenmiş özelliklerdir. Belirli bir sorun yaşıyorsanız, neyi denediğinizi ve neyin yanlış gittiğini açıklayan kendi sorunuzu gönderin.
wjans

3
@ user836026: Evet, arka plan modunu belirterek bunu kastediyorum. Konum güncellemesini durdurduktan sonra, uygulamanın sonlandırılmasını önlemek için 10 dakika içinde yeniden başlatılması gerekir.
wjans

12
Burada tartışılanları yansıtan bazı gerçek kodları görmek isteyen herkes için stackoverflow.com/questions/10235203/…
Lolo

55

On iOS 8/9/10 her 5 dakikada aşağıdakileri yapın arka planda konum güncelleme yapmak için:

  1. Proje -> Yetenekler -> Arka Plan Modları -> Konum güncellemelerini seçin

  2. Project -> Info -> anahtarını ekleyin NSLocationAlwaysUsageDescription öğesini boş değere (veya isteğe bağlı olarak herhangi bir metne) ekleyin

  3. Uygulamanız arka planda olduğunda konumun çalışmasını sağlamak ve web hizmetine koordinatlar göndermek veya her 5 dakikada bir onlarla bir şey yapmak için aşağıdaki kodda olduğu gibi uygulayın.

Herhangi bir arka plan görevi veya zamanlayıcı kullanmıyorum. Bu kodu, uygulamam arka planda çalışan birkaç saat masamda yatan iOS 8.1 ile cihazımla test ettim. Cihaz kilitlendi ve kod her zaman düzgün çalışıyordu.

@interface LocationManager () <CLLocationManagerDelegate>
@property (strong, nonatomic) CLLocationManager *locationManager;
@property (strong, nonatomic) NSDate *lastTimestamp;

@end

@implementation LocationManager

+ (instancetype)sharedInstance
{
    static id sharedInstance = nil;

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[self alloc] init];
        LocationManager *instance = sharedInstance;
        instance.locationManager = [CLLocationManager new];
        instance.locationManager.delegate = instance;
        instance.locationManager.desiredAccuracy = kCLLocationAccuracyBest; // you can use kCLLocationAccuracyHundredMeters to get better battery life
        instance.locationManager.pausesLocationUpdatesAutomatically = NO; // this is important
    });

    return sharedInstance;
}

- (void)startUpdatingLocation
{
    CLAuthorizationStatus status = [CLLocationManager authorizationStatus];

    if (status == kCLAuthorizationStatusDenied)
    {
        NSLog(@"Location services are disabled in settings.");
    }
    else
    {
        // for iOS 8
        if ([self.locationManager respondsToSelector:@selector(requestAlwaysAuthorization)])
        {
            [self.locationManager requestAlwaysAuthorization];
        }
        // for iOS 9
        if ([self.locationManager respondsToSelector:@selector(setAllowsBackgroundLocationUpdates:)])
        {
            [self.locationManager setAllowsBackgroundLocationUpdates:YES];
        }

        [self.locationManager startUpdatingLocation];
    }
}

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
    CLLocation *mostRecentLocation = locations.lastObject;
    NSLog(@"Current location: %@ %@", @(mostRecentLocation.coordinate.latitude), @(mostRecentLocation.coordinate.longitude));

    NSDate *now = [NSDate date];
    NSTimeInterval interval = self.lastTimestamp ? [now timeIntervalSinceDate:self.lastTimestamp] : 0;

    if (!self.lastTimestamp || interval >= 5 * 60)
    {
        self.lastTimestamp = now;
        NSLog(@"Sending current location to web service.");
    }
}

@end

3
Bu birkaç saatten fazla çalışıyor mu? Uygulama sonlandırılmasa bile, cihaz uygulamanın arka plana gitmesinden yaklaşık bir saat sonra konum güncellemelerini itmeyi durdurur.
Mikstic

2
Projemde Arka Plan Getirme devre dışı bırakıldı (kapalı veya açık olması önemli değil). Ancak pausesLocationUpdatesOtomatik olarak yukarıdaki örneğin düzgün çalışması için NO değerine ayarlanmalıdır. Daha önce sistem tarafından duraklatılmışsa, tekrar hareket etmeye başladığınızda otomatik olarak DEVAM ETMEYECEKTİR. Bu nedenle yukarıdaki örnekte bu özelliği HAYIR olarak ayarladım.
Leszek Szary

1
Bence kesinlikle haklısın. Sorun hakkında daha fazla bilgiyi burada bulabilirsiniz: stackoverflow.com/q/17484352/1048331
Myxtic

2
@LeszekS arka plan için iOS 9 desteği için aşağıdaki kodu eklemeniz gerekiyor - if ([self.locationManager respondsToSelector:@selector(setAllowsBackgroundLocationUpdates:)]) { [self.locationManager setAllowsBackgroundLocationUpdates:YES]; }
Fenil

2
Bunu yapmanın rahatlığı nedir? Hala yüksek bir hassasiyetle pili tüketiyorsunuz. Yapmadığınız tek şey , 5 dakikalık bir aralığa ulaşana kadar zaten alınan konumu kullanmamanız ...
Honey

34

Bunu geliştirdiğim bir uygulamada yaptım. Uygulama arka planda olduğunda zamanlayıcılar çalışmaz, ancak uygulama konum güncellemelerini sürekli olarak alır. Belgede bir yerde okudum (şimdi bulamıyorum, yaptığımda bir güncelleme göndereceğim) bir yöntem, uygulama arka planda olduğunda yalnızca etkin bir çalışma döngüsünde çağrılabilir. Uygulama temsilcisi, bg'de bile etkin bir çalışma döngüsüne sahiptir, bu nedenle bu işi yapmak için kendi oluşturmanıza gerek yoktur. [Bu doğru açıklama olup olmadığından emin değilim ama okuduğumdan nasıl anladım]

Her şeyden önce, locationanahtarın nesnesini UIBackgroundModesuygulamanızın info.plist'e ekleyin. Şimdi yapmanız gereken, uygulamanızın herhangi bir yerinde konum güncellemelerini başlatmaktır:

    CLLocationManager locationManager = [[CLLocationManager alloc] init];
    locationManager.delegate = self;//or whatever class you have for managing location
    [locationManager startUpdatingLocation];

Ardından, -(void)didUpdateToLocation:(CLLocation*)locationuygulama temsilcisine konum güncellemelerini işlemek için bir yöntem yazın . Ardından yöntemi uygulamak locationManager:didUpdateLocation:fromLocationait CLLocationManagerDelegate(biz 'öz' olarak yer yöneticisi temsilci ayarlamak beri) konum yöneticisini başladı ettiği sınıfta. Bu yöntemin içinde, konum güncellemelerini işlemek için gereken zaman aralığının geçip geçmediğini kontrol etmeniz gerekir. Bunu her seferinde geçerli saati kaydederek yapabilirsiniz. Bu süre geçtiyse, uygulama temsilcinizden UpdateLocation yöntemini çağırın:

NSDate *newLocationTimestamp = newLocation.timestamp;
NSDate *lastLocationUpdateTiemstamp;

int locationUpdateInterval = 300;//5 mins

NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
if (userDefaults) {

        lastLocationUpdateTiemstamp = [userDefaults objectForKey:kLastLocationUpdateTimestamp];

        if (!([newLocationTimestamp timeIntervalSinceDate:lastLocationUpdateTiemstamp] < locationUpdateInterval)) {
            //NSLog(@"New Location: %@", newLocation);
            [(AppDelegate*)[UIApplication sharedApplication].delegate didUpdateToLocation:newLocation];
            [userDefaults setObject:newLocationTimestamp forKey:kLastLocationUpdateTimestamp];
        }
    }
}

Bu, uygulamanız arka planda olsa bile yönteminizi her 5 dakikada bir çağıracaktır. Imp: Bu uygulama pili tüketir, konum verilerinizin doğruluğu kritik değilse kullanmanız gerekir[locationManager startMonitoringSignificantLocationChanges]

Bunu uygulamanıza eklemeden önce lütfen Konum Tanıma Programlama Kılavuzu'nu okuyun


3
Bu şekilde konum hizmetleri sürekli olarak etkinleştirilir (gerçekten pil boşaltma), bunu istemiyorum. Her n dakikada bir konum hizmetini etkinleştirmek ve iyi bir düzeltme olduğunda hemen devre dışı bırakmak istiyorum (sadece bunu açıkça sorumu açıklamamıştım fark ettim). Bu davranışı tarif ettiğim çözümde başarabilirim.
wjans

3
Konum yöneticisi doğruluğunu 1 km'ye ayarlayabilirsiniz - bu, pilinizi neredeyse bozulmadan bırakır. 5 dakika sonra doğruluğu 1 metreye ayarlarsınız. Konumu tatmin ederseniz (normalde 5 saniye sonra) doğruluğu tekrar 1 km'ye ayarlayın.
knagode

knagode, pil boşaltma sorunu için önerilen çözümü denedim, ancak N dakika sonra doğruluğu artırdıktan sonra bile locationManager: didUpdateLocations yöntemi tekrar çağrılmaz. StartUpdating ve stopUpdating'i denedim, doğruluğu artırmak ve azaltmak yerine, başarıyla delege locationManager olarak adlandırıldı: N dakika sonra didUpdateLocations yöntemi, ancak Arka Plan MODU'nda çalışmıyor ...
Hardik Darji

Doküman bağlantılarına gelince. Buraya bakın : "Temsilci nesnenizin yöntemleri, karşılık gelen konum hizmetlerini başlattığınız iş parçacığından çağrılır. Bu iş parçacığının, uygulamanızın ana iş parçacığında bulunan gibi etkin bir çalışma döngüsü olması gerekir."
Bal

24

Artık iOS6, sonsuza kadar çalışan bir konum hizmetine sahip olmanın en iyi yoludur ...

- (void)applicationWillResignActive:(UIApplication *)application
{
/*
 Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
 Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
 */

NSLog(@"to background");

app.isInBackground = TRUE;

UIApplication *app = [UIApplication sharedApplication];

// Request permission to run in the background. Provide an
// expiration handler in case the task runs long.
NSAssert(bgTask == UIBackgroundTaskInvalid, nil);

bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
    // Synchronize the cleanup call on the main thread in case
    // the task actually finishes at around the same time.
    dispatch_async(dispatch_get_main_queue(), ^{

        if (bgTask != UIBackgroundTaskInvalid)
        {
            [app endBackgroundTask:bgTask];
            bgTask = UIBackgroundTaskInvalid;
        }
    });
}];

// Start the long-running task and return immediately.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

    // Do the work associated with the task.

    locationManager.distanceFilter = 100;
    locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;
    [locationManager startMonitoringSignificantLocationChanges];
    [locationManager startUpdatingLocation];

    NSLog(@"App staus: applicationDidEnterBackground");
    // Synchronize the cleanup call on the main thread in case
    // the expiration handler is fired at the same time.
    dispatch_async(dispatch_get_main_queue(), ^{
        if (bgTask != UIBackgroundTaskInvalid)
        {
            [app endBackgroundTask:bgTask];
            bgTask = UIBackgroundTaskInvalid;
        }
    });
});

NSLog(@"backgroundTimeRemaining: %.0f", [[UIApplication sharedApplication] backgroundTimeRemaining]);

}

Sadece böyle test ettim:

Uygulamayı başlattım, arka plana gidin ve birkaç dakika arabada hareket ettim. Sonra 1 saat boyunca eve gidip tekrar hareket etmeye başlıyorum (uygulamayı tekrar açmadan). Konumlar yeniden başladı. Sonra iki saat durdu ve tekrar başladı. Yine herşey yolunda ...

İOS6'da yeni konum hizmetlerini KULLANMAYI UNUTMAYIN

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{   
    CLLocation *loc = [locations lastObject];

    // Lat/Lon
    float latitudeMe = loc.coordinate.latitude;
    float longitudeMe = loc.coordinate.longitude;
}

1
Uygulama çökerse veya öldürülürse sistem yeniden başlamaz, değil mi?
Ken

1
Daha okunabilir kod için, [locations lastObject] konumları yapabilirsiniz; [locations objectAtIndex yerine: [konum sayısı] - 1]
axello

yönteminiz sadece ios6'da mı?
pengwang

Bunu kullanmak zorunda mıyız? Ben sadece bir sınıfın viewDidLoad konum yöneticisi kodu var düşündüm, ve plist dosya arka plan anahtarı Konum güncellemeleri için uygulama kayıtları yeterince iyi olması gerektiğini belirledim? Lütfen bana bunda yardım eder misin!
nithinreddy

Evet, çekicilik nithinreddy gibi çalışacak, ancak iOS bu süreden sonra uzun iplikleri öldürdüğü için 10 dakika sonra çalışmayı durduracak. Bu hizmetlerin sonsuza dek başlatılmasını istiyorsanız çözümüm mükemmel. İki gün önce bazı testler yaptım ve her saat% 6 pil tüketiyor. Bunun yerine [locationManager startUpdatingLocation] kullanıldığında% 17 azalır
Alejandro Luengo

13

Kabusu olan başka birine bunu anlayın. Basit bir çözümüm var.

  1. raywenderlich.com bu örneğe bakın -> örnek kod var, bu mükemmel çalışıyor, ama ne yazık ki arka plan konumu sırasında hiçbir zamanlayıcı. bu süresiz olarak devam eder.
  2. Şunu kullanarak zamanlayıcı ekleyin:

    -(void)applicationDidEnterBackground {
    [self.locationManager stopUpdatingLocation];
    
    UIApplication*    app = [UIApplication sharedApplication];
    
    bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
        [app endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    }];
    
     self.timer = [NSTimer scheduledTimerWithTimeInterval:intervalBackgroundUpdate
                                                  target:self.locationManager
                                                selector:@selector(startUpdatingLocation)
                                                userInfo:nil
                                                 repeats:YES];
    
    }
  3. İnfo.plist'e "Konum güncellemeleri için uygulama kayıtları" eklemeyi unutmayın.


Bu konum 3 dakikadan fazla sürüyor mu?
SleepNot

Yetenekler -> Arka Plan Modunda konum ayarlanmalıdır.
Sergio Andreotti

Çalışmıyor!! İOS 8 ve iOS 10
Kirti

Yanlışsam düzelt. Okuduğum şeye göre locationManager'ı yalnızca bir kez başlatıyorsunuz. Bundan sonra tüm aralıklar gereksizdir. Çoktan başladığından beri
Honey

çalışıyor ancak 170 saniye sonra durduruluyor. görevimi sınırsız bir süre için arka planda çalıştırmak istiyorum
anshuman burmman

8

İşte ne kullanıyorum:

import Foundation
import CoreLocation
import UIKit

class BackgroundLocationManager :NSObject, CLLocationManagerDelegate {

    static let instance = BackgroundLocationManager()
    static let BACKGROUND_TIMER = 150.0 // restart location manager every 150 seconds
    static let UPDATE_SERVER_INTERVAL = 60 * 60 // 1 hour - once every 1 hour send location to server

    let locationManager = CLLocationManager()
    var timer:NSTimer?
    var currentBgTaskId : UIBackgroundTaskIdentifier?
    var lastLocationDate : NSDate = NSDate()

    private override init(){
        super.init()
        locationManager.delegate = self
        locationManager.desiredAccuracy = kCLLocationAccuracyKilometer
        locationManager.activityType = .Other;
        locationManager.distanceFilter = kCLDistanceFilterNone;
        if #available(iOS 9, *){
            locationManager.allowsBackgroundLocationUpdates = true
        }

        NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.applicationEnterBackground), name: UIApplicationDidEnterBackgroundNotification, object: nil)
    }

    func applicationEnterBackground(){
        FileLogger.log("applicationEnterBackground")
        start()
    }

    func start(){
        if(CLLocationManager.authorizationStatus() == CLAuthorizationStatus.AuthorizedAlways){
            if #available(iOS 9, *){
                locationManager.requestLocation()
            } else {
                locationManager.startUpdatingLocation()
            }
        } else {
                locationManager.requestAlwaysAuthorization()
        }
    }
    func restart (){
        timer?.invalidate()
        timer = nil
        start()
    }

    func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
        switch status {
        case CLAuthorizationStatus.Restricted:
            //log("Restricted Access to location")
        case CLAuthorizationStatus.Denied:
            //log("User denied access to location")
        case CLAuthorizationStatus.NotDetermined:
            //log("Status not determined")
        default:
            //log("startUpdatintLocation")
            if #available(iOS 9, *){
                locationManager.requestLocation()
            } else {
                locationManager.startUpdatingLocation()
            }
        }
    }
    func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {

        if(timer==nil){
            // The locations array is sorted in chronologically ascending order, so the
            // last element is the most recent
            guard let location = locations.last else {return}

            beginNewBackgroundTask()
            locationManager.stopUpdatingLocation()
            let now = NSDate()
            if(isItTime(now)){
                //TODO: Every n minutes do whatever you want with the new location. Like for example sendLocationToServer(location, now:now)
            }
        }
    }

    func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
        CrashReporter.recordError(error)

        beginNewBackgroundTask()
        locationManager.stopUpdatingLocation()
    }

    func isItTime(now:NSDate) -> Bool {
        let timePast = now.timeIntervalSinceDate(lastLocationDate)
        let intervalExceeded = Int(timePast) > BackgroundLocationManager.UPDATE_SERVER_INTERVAL
        return intervalExceeded;
    }

    func sendLocationToServer(location:CLLocation, now:NSDate){
        //TODO
    }

    func beginNewBackgroundTask(){
        var previousTaskId = currentBgTaskId;
        currentBgTaskId = UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler({
            FileLogger.log("task expired: ")
        })
        if let taskId = previousTaskId{
            UIApplication.sharedApplication().endBackgroundTask(taskId)
            previousTaskId = UIBackgroundTaskInvalid
        }

        timer = NSTimer.scheduledTimerWithTimeInterval(BackgroundLocationManager.BACKGROUND_TIMER, target: self, selector: #selector(self.restart),userInfo: nil, repeats: false)
    }
}

İzlemeyi AppDelegate'te şöyle başlattım:

BackgroundLocationManager.instance.start()

Teşekkürler. Projemizin kullanıcı konumunu izlemesi ve PubNub etkinliklerini arka planda göndermesi gerekiyor ve çözümünüz gerçekten iyi çalışıyor.
user921509

Merhaba Hmitkov, Sunucudaki kullanıcı konumunu göndermek için sendLocationToServer yöntemini
çağırabilirim

@ AmanGupta007 sendLocationToServer işlevini locationManager'da (yönetici: didUpdateLocations :) arayabilirsiniz. Koddaki // TODO yorumuna dikkat edin.
hmitkov

@hmitkov Uygulama arka planda iken Konum Servislerini bu şekilde başlatmak ve durdurmak mümkün mü? Örneğin, konum hizmetini bir push bildiriminden başlatın, enlem / boylam alın, web hizmetine gönderin, ardından konumu güncellemeyi durdurun. Bunu, 'içerik kullanılabilir' = 1 push gövdesine her eklendiğinde yapın.
DookieMan

1
Bu kodu denedim ama iOS11'de çalışmıyor gibi görünüyor? (Başka bir sürümde test etmedim.)
Tom

6

Ne yazık ki, tüm varsayımlarınız doğru görünüyor ve bunu yapmanın bir yolu olduğunu sanmıyorum. Pil ömründen tasarruf etmek için iPhone'un konum servisleri harekete dayalıdır. Telefon bir noktada oturursa, konum servisleri tarafından görülmez.

CLLocationManagerSadece arayacaklocationManager:didUpdateToLocation:fromLocation: telefon üç konum hizmetlerinden biri (baz istasyonu, gps, wifi) bir değişiklik algıladığı takdirde sadece olur konum güncellemesi, aldığında.

Daha fazla çözümün bilgilendirilmesine yardımcı olabilecek birkaç şey:

  • Hizmetlerin başlatılması ve durdurulması, didUpdateToLocationtemsilci yönteminin çağrılmasına neden olur , ancak newLocationeski bir zaman damgası olabilir.

  • Bölge İzleme yardımcı olabilir

  • Arka planda çalışırken, Apple tarafından onaylanan "tam" LocationServices desteğini almanın zor olabileceğini unutmayın. Gördüğüm kadarıyla, startMonitoringSignificantLocationChangesarka plan konum desteğine ihtiyaç duyan uygulamalar için özellikle düşük güçlü bir alternatif olarak tasarlandılar ve uygulama kesinlikle buna ihtiyaç duymadıkça geliştiricileri bunu kullanmaya teşvik ediyorlar.

İyi şanslar!

GÜNCELLEME: Bu düşünceler güncelliğini yitirmiş olabilir. Sanki insanlar @wjans cevabı ile başarılı oluyor gibi görünüyor.


1
AppStore'da arka planda bir konum güncellemesi almayı mümkün kılan uygulamalar (örneğin "Konumum" gibi) bulunmaktadır. Konum hizmetini etkin tutmazlar, ancak tanımlanan aralığa göre kısa bir süre içinde etkinleştirilirler. Bunu nasıl yapıyorlar?
wjans

2
Açıkladığınız durumda, uygulama büyük olasılıkla startMonitoringSignificantLocationChanges yaklaşımını kullanıyor. Burada, bir konum güncellemesi alındığında telefon geçici olarak 'uyandırılır', ancak bu hizmeti arka planda 'ping' yapmak için ayarlayabileceğiniz bir aralık yoktur. Telefon hareket ettiğinde (veya Hücresel moddan GPS veya Wifi moduna geçtiğinde), bir güncellemeyi tetikler. Konuyla ilgili Stanford iTunes U konferansı benim için gerçekten yararlı oldu - umarım bir çözüm bulmanıza yardımcı olabilir: itunes.apple.com/us/itunes-u/iphone-application-development/…
Chazbot

2
İşaretçi için teşekkürler. Ancak, yine de uygulamanın ne yaptığını anlamıyorum. Telefonum masamda olsa bile hiç hareket etmiyorsa, konum servisinin her 10 dakikada bir tetiklendiğini görebiliyorum (tam olarak). Doğru anlarsam, startMonitoringSignificantLocationChangesbu durumda herhangi bir güncelleme yapmazdım.
wjans

1
@wjans telefonunuzun pil tüketimi nasıl, belki de Mylocus uygulaması nedeniyle hızlı bir şekilde tükendiğini fark ettiniz mi?
user836026

6

Konum hizmetlerini kullanarak bir uygulama yazdım, uygulama her 10 saniyede bir konum göndermelidir. Ve çok iyi çalıştı.

" AllowDeferredLocationUpdatesUntilTraveled: zaman aşımı"Apple'ın izleyerek " yöntemini .

Ne yaptım:

Gereklidir: Güncelleme Konumu için arka plan modunu kaydedin.

1. oluşturma LocationMangerve startUpdatingLocationbirlikte,accuracy ve filteredDistancene istersen olarak:

-(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. kullanarak uygulama sonsuza kadar çalıştırmak tutmak içinallowDeferredLocationUpdatesUntilTraveled:timeout Arka planda yöntemi arka plana updatingLocationgeçtiğinde yeni parametre ile yeniden başlatmanız gerekir , örneğin:

- (void)applicationWillResignActive:(UIApplication *)application {
     _isBackgroundMode = YES;

    [_locationManager stopUpdatingLocation];
    [_locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
    [_locationManager setDistanceFilter:kCLDistanceFilterNone];
    _locationManager.pausesLocationUpdatesAutomatically = NO;
    _locationManager.activityType = CLActivityTypeAutomotiveNavigation;
    [_locationManager startUpdatingLocation];
 }

3. Uygulama güncellenir locationManager:didUpdateLocations:Geri arama ile normal olarak konumlar :

-(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. Ama locationManager:didFinishDeferredUpdatesWithError:amaçlarınız için geri aramada veri işlemek gerekir

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

     _deferringUpdates = NO;

     //do something 
}

5. NOT:LocationManager Uygulamanın arka plan / forground modu arasında her geçişinde parametreleri sıfırlamamız gerektiğini düşünüyorum .


@Bu veya bu wjans'ın açıkladığı çözümün tüm çözümlerinden hangisi cihaz pilinden tasarruf etmeyi tercih etmeli yoksa her ikisi de aynı şeyi etkiler mi?
Yash

2
Bahsettiğiniz iki çözümü de denedim ve @ wjans'ın biraz daha fazla pil tasarrufu olduğunu gördüm. Ancak, iOS 8'in gelmesinden sonra, bu çözümün artık düzgün çalışmadığı görülüyor. Daha fazla ayrıntı için: Çoğu zaman, uygulama arka plan modunda uzun süre yaşayamaz.
samthui7

@ samthui7 Neden pausesLocationUpdatesAutomatically = false ayarlıyorsunuz?
CedricSoubrie

Uygulamamın gereksinimi, her 10 saniyede bir userLocation göndermeye devam ederken pausesLocationUpdatesAutomatically = truekonum yöneticisine yer değişikliği görünmüyorsa güncellemeleri duraklatmasını söyler ( Apple'ın dokümanı ). Her neyse, location manager pauses updateshenüz durumu açıkça test etmedim: D.
samthui7

@CedricSoubrie: Ayar pausesLocationUpdatesAutomatically = true, uygulamamın konum güncelleme işlemini durdurmasına neden oluyor.
samthui7

5
if ([self.locationManager respondsToSelector:@selector(setAllowsBackgroundLocationUpdates:)]) {
    [self.locationManager setAllowsBackgroundLocationUpdates:YES];
}

Bu, iOS 9'dan beri arka plan konum izleme için gereklidir.


1
Bu benim günümü kurtardı! iOS9 cihazıyla iOS8 dağıtım hedefini kullanma
Yaro

4

Xs2bush'ın bir aralık elde etme yöntemini kullandım ( timeIntervalSinceDate ) ve biraz genişlettim. İhtiyacım olan gerekli doğruluğu elde ettiğimden ve gps radyoyu gereğinden fazla açık tutarak pili bitirmediğimden emin olmak istedim.

Aşağıdaki ayarlarla konumun sürekli çalışmasını sağlıyorum:

locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers;
locationManager.distanceFilter = 5;

bu, bataryadaki nispeten düşük bir boşaltmadır. Bir sonraki periyodik konum okumamı almaya hazır olduğumda, önce konumun istenen doğrulukta olup olmadığını kontrol ediyorum, eğer öyleyse, konumu kullanıyorum. Değilse, bununla doğruluğu arttırırım:

locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters;
locationManager.distanceFilter = 0;

konumumu alın ve sonra konuma sahip olduktan sonra pilin boşalmasını en aza indirmek için doğruluğu tekrar aşağı çevirdim. Ben bu tam çalışan bir örnek yazdım ve ayrıca konum veri toplamak, bir veritabanına depolamak ve kullanıcıların gerçek zamanlı olarak gps verilerini görüntülemek veya daha önce saklanan yolları almak ve görüntülemek için sunucu tarafı kodu için kaynak yazdım. İOS, android, windows phone ve java için istemcilerim var. Tüm müşteriler yerel olarak yazılmıştır ve hepsi arka planda düzgün çalışır. Proje MIT lisanslıdır.

İOS projesi, iOS 7'nin temel SDK'sını kullanarak iOS 6'yı hedefliyor . Kodu buradan alabilirsiniz .

Bununla ilgili herhangi bir sorun görürseniz lütfen github'da bir sorun oluşturun. Teşekkürler.


Çözümünüzü denedim, ama çalışmıyor ... Uygulama arka plana gittiğinde, doğruluğu artırdıktan sonra bile, uygulama benim durumumda didUpdateToLocation yöntemi olarak adlandırılmadı
Hardik Darji

@HardikDarji önemli bir soru. Hareket ediyor musun? Değilse, konum güncellemeleri durabilir. Telefonunuzu bir yürüyüş veya sürüş için çıkarmayı deneyin ve bunun düzeltilip düzeltilmediğine bakın.
nickfox

Hızlı cevabın için teşekkürler. Ama telefonumun hareket etmediği veya hareket etmediği her 2 dakikada bir konum güncellemeleri istiyorum. Bu senaryoda didUpdateToLocation yöntemi çağrılmaz. Ben burada arıyorum: Her n dakikada bir konum güncellemesi nasıl alabilirim !!!
Hardik Darji

timeIntervalInSeconds değerini 120 olarak ayarlamayı deneyin ve bu satırı uncomment edin: locationManager.pausesLocationUpdatesAutomatically = NO;
nickfox

1
Yukarıdaki yorumda yayınladığınız çözüm pil ömrünü öldürüyor mu yoksa bunun için hala optimizasyon yapıyor musunuz?
samfr

2

Görünüşe göre stopUpdatingLocation, arka plan bekçi zamanlayıcısını tetikleyen şeydir, bu yüzden didUpdateLocation ile değiştirdim:

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

GPS'i etkin bir şekilde kapatıyor gibi görünüyor. NSTimer arka planı için seçici şu şekilde olur:

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

Yaptığım tek şey, her birkaç dakikada bir yüksek doğruluklu bir koordinat elde etmek için doğruluğu düzenli olarak değiştirmek ve locationManager durdurulmadığından, backgroundTimeRemaining maksimum değerinde kalır. Bu, pil tüketimini saatte ~% 10'dan (arka planda sabit kCLLocationAccuracyBest ile) cihazımda saatte ~% 2'ye düşürdü


2

İstenen konum doğruluğu ile her n saniyede bir arka plan konum güncellemeleri almayı sağlayan bir cocoapod APScheduledLocationManager vardır .

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

Depo ayrıca Swift 3'te yazılmış bir örnek uygulama içeriyor.


Hedef c'de böyle bir şey var mı?
Moxarth

1

İOS 9 ve watchOS 2.0'da, CLLocationManager'da geçerli konumu istemenizi sağlayan yeni bir yöntem vardır: CLLocationManager: requestLocation (). Bu hemen tamamlanır ve konumu CLLocationManager temsilcisine döndürür.

NSTimer'i şimdi bu yöntemle her dakika bir konum istemek için kullanabilirsiniz ve startUpdatingLocation ve stopUpdatingLocation yöntemleriyle çalışmak zorunda değilsiniz.

Ancak, son konumdan X metre değişikliğe dayalı konumları yakalamak istiyorsanız, CLLocationManger'ın distanceFilter özelliğini ayarlayın ve X, startUpdatingLocation () çağrısını yapın.


-1

Ekli bir Swift çözümüdür:

App registers for location updatesİnfo.plist'de tanımlayın

LocationManager'ı her zaman çalışır durumda tutun

Anahtarı kCLLocationAccuracyarasındaki BestForNavigation(5 saniye yerini almak için) ve ThreeKilometersönlemek pil drenaj için bekleme süresinin geri kalanında

Bu örnek, Ön Plan'daki her 1 dakikada bir ve Arka Plandaki her 15 dakikada bir konumu güncelleştirir.

Örnek, bir iOS 7 cihazında çalışan Xcode 6 Beta 6 ile iyi çalışıyor.

Uygulama Temsilcisi'nde (mapView, mapView Denetleyicisini gösteren bir İsteğe Bağlı)

func applicationDidBecomeActive(application: UIApplication!) {
    if appLaunched! == false { // Reference to mapView used to limit one location update per timer cycle
        appLaunched = true
        var appDelegate = UIApplication.sharedApplication().delegate as AppDelegate
        var window = appDelegate.window
        var tabBar = window?.rootViewController as UITabBarController
        var navCon = tabBar.viewControllers[0] as UINavigationController
        mapView = navCon.topViewController as? MapViewController
    }
    self.startInitialPeriodWithTimeInterval(60.0)
}

func applicationDidEnterBackground(application: UIApplication!) {
    self.startInitialPeriodWithTimeInterval(15 * 60.0)
}

func startInitialPeriodWithTimeInterval(timeInterval: NSTimeInterval) {
    timer?.invalidate() // reset timer
    locationManager?.desiredAccuracy = kCLLocationAccuracyBestForNavigation
    timer = NSTimer.scheduledTimerWithTimeInterval(5.0, target: self, selector: Selector("getFirstLocationUpdate:"), userInfo: timeInterval, repeats: false)
}

func getFirstLocationUpdate(sender: NSTimer) {
    let timeInterval = sender.userInfo as Double
    timer?.invalidate()
    mapView?.canReportLocation = true
    timer = NSTimer.scheduledTimerWithTimeInterval(timeInterval, target: self, selector: Selector("waitForTimer:"), userInfo: timeInterval, repeats: true)
}

func waitForTimer(sender: NSTimer) {
    let time = sender.userInfo as Double
    locationManager?.desiredAccuracy = kCLLocationAccuracyBestForNavigation
    finalTimer = NSTimer.scheduledTimerWithTimeInterval(5.0, target: self, selector: Selector("getLocationUpdate"), userInfo: nil, repeats: false)
}

func getLocationUpdate() {
    finalTimer?.invalidate()
    mapView?.canReportLocation = true
}

MapView'de (locationManager, AppDelegate'teki nesneyi gösterir)

override func viewDidLoad() {
    super.viewDidLoad()
    var appDelegate = UIApplication.sharedApplication().delegate! as AppDelegate
    locationManager = appDelegate.locationManager!
    locationManager.delegate = self
    canReportLocation = true
}

  func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
        if canReportLocation! {
            canReportLocation = false
            locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
        } else {
            //println("Ignore location update")
        }
    }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.