Bagaimana cara mendapatkan pembaruan lokasi latar belakang setiap n menit di aplikasi iOS saya?

207

Saya mencari cara untuk mendapatkan pembaruan lokasi latar belakang setiap n menit di aplikasi iOS saya. Saya menggunakan iOS 4.3 dan solusinya harus berfungsi untuk iPhone yang tidak dipenjara.

Saya mencoba / mempertimbangkan opsi berikut:

  • CLLocationManager startUpdatingLocation/startMonitoringSignificantLocationChanges: Ini berfungsi di latar belakang seperti yang diharapkan, berdasarkan pada properti yang dikonfigurasikan, tetapi tampaknya tidak mungkin untuk memaksanya memperbarui lokasi setiap n menit
  • NSTimer: Tidak berfungsi saat aplikasi berjalan di latar depan tetapi tampaknya tidak dirancang untuk tugas latar belakang
  • Pemberitahuan lokal: Pemberitahuan lokal dapat dijadwalkan setiap n menit, tetapi tidak mungkin untuk mengeksekusi beberapa kode untuk mendapatkan lokasi saat ini (tanpa pengguna harus meluncurkan aplikasi melalui pemberitahuan). Pendekatan ini juga tampaknya tidak menjadi pendekatan yang bersih karena ini bukan untuk apa notifikasi harus digunakan.
  • UIApplication:beginBackgroundTaskWithExpirationHandler: Sejauh yang saya mengerti, ini harus digunakan untuk menyelesaikan beberapa pekerjaan di latar belakang (juga terbatas waktu) ketika aplikasi dipindahkan ke latar belakang daripada menerapkan proses latar belakang "jangka panjang".

Bagaimana saya bisa menerapkan pembaruan lokasi latar belakang biasa ini?

wan
sumber
tindak lanjut yang bermanfaat: stackoverflow.com/questions/10235203/…
Lolo
1
Jika Anda mencoba membuatnya berfungsi di iOS 7, Anda dapat mencoba solusi ini di sini: stackoverflow.com/questions/18946881/... Jika Anda memiliki pertanyaan, Anda dapat bergabung dengan kami untuk diskusi di sini: mobileoop.com/background -lokasi-pembaruan-pemrograman-untuk-ios-7
Ricky
1
Semua temuan Anda benar (empat poin utama). Informasi berharga itu kemudian, mengetahui apa yang tidak cocok Anda gunakan kasing? Dan ya, ketika dalam SUSPENDED MODE atau NOT RUNNING, tidak ada metode utama untuk memperbarui setiap n-menit.
LenArt

Jawaban:

113

Saya menemukan solusi untuk mengimplementasikannya dengan bantuan Forum Pengembang Apple:

  • Menentukan location background mode
  • Buat NSTimerdi latar belakang denganUIApplication:beginBackgroundTaskWithExpirationHandler:
  • Ketika nini lebih kecil daripada UIApplication:backgroundTimeRemainingitu akan bekerja dengan baik. Ketika nadalah lebih besar , yang location managerharus diaktifkan (dan dinonaktifkan) lagi sebelum ada waktu yang tersisa untuk menghindari tugas latar belakang dibunuh.

Ini berfungsi karena lokasi adalah salah satu dari tiga jenis eksekusi latar belakang yang diizinkan .

Catatan: Saya kehilangan waktu dengan menguji ini di simulator yang tidak berfungsi. Namun, itu berfungsi dengan baik di ponsel saya.

wan
sumber
24
apakah Anda memiliki tautan ke forum. Saya mencari untuk menerapkan jenis pemetaan lokasi yang sama dan belum dapat membuatnya berfungsi. Atau beberapa kode contoh akan sangat dihargai.
utahwithak
4
Bisakah Anda jelaskan mengapa tugas latar belakang tidak dimatikan setelah 10 menit (waktu maksimum yang diizinkan) jika Anda hanya berhenti dan memulai pengelola lokasi? apakah itu semacam fungsi yang dimaksudkan? kedengarannya lebih seperti bug di Apple SDK jika itu terjadi seperti itu. Di versi iOS mana Anda mencobanya?
saurabh
5
@all: Ya, aplikasi kami tersedia di AppStore. Saya tidak akan memposting semua kode, semua bulletpoint yang disebutkan adalah fitur yang terdokumentasi dengan jelas. Jika Anda mengalami masalah tertentu, poskan pertanyaan Anda sendiri menjelaskan apa yang telah Anda coba dan apa yang salah.
wjans
3
@ user836026: Ya, itulah yang saya maksud dengan menentukan mode latar belakang. Setelah menghentikan pembaruan lokasi, itu harus dimulai lagi dalam waktu 10 menit untuk menghindari aplikasi dihentikan.
wjans
12
Untuk semua yang tertarik melihat beberapa kode aktual yang mencerminkan apa yang sedang dibahas di sini, periksa stackoverflow.com/questions/10235203/…
Lolo
55

Pada iOS 8/9/10 untuk membuat pembaruan lokasi latar belakang setiap 5 menit lakukan hal berikut:

  1. Pergi ke Proyek -> Kemampuan -> Mode Latar Belakang -> pilih Pembaruan lokasi

  2. Pergi ke Project -> Info -> tambahkan NSLocationAlwaysUsageDescription kunci dengan nilai kosong (atau opsional teks apa pun)

  3. Untuk membuat lokasi berfungsi ketika aplikasi Anda berada di latar belakang dan mengirim koordinat ke layanan web atau melakukan apa saja dengan mereka setiap 5 menit menerapkannya seperti dalam kode di bawah ini.

Saya tidak menggunakan tugas atau timer latar belakang. Saya telah menguji kode ini dengan perangkat saya dengan iOS 8.1 yang berbaring di meja saya selama beberapa jam dengan aplikasi saya berjalan di latar belakang. Perangkat dikunci dan kode berjalan dengan benar sepanjang waktu.

@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
Leszek Szary
sumber
3
Apakah ini bekerja lebih dari beberapa jam? Sepertinya bahkan jika aplikasi tidak dihentikan, perangkat berhenti mendorong pembaruan lokasi setelah satu jam atau lebih dari aplikasi pergi ke latar belakang.
Myxtic
2
Background Fetch dinonaktifkan di proyek saya (tidak masalah apakah itu mati atau hidup). Namun pausesLocationUpdatesAutomatis harus disetel ke NO agar contoh di atas berfungsi dengan baik. Ini TIDAK akan melanjutkan secara otomatis setelah Anda mulai bergerak lagi jika dihentikan oleh sistem sebelumnya. Itulah sebabnya dalam contoh di atas saya mengatur properti ini menjadi TIDAK.
Leszek Szary
1
Saya pikir Anda benar sekali.
Temukan
2
@LeszekS Anda perlu menambahkan kode di bawah ini untuk dukungan iOS 9 untuk latar belakang - if ([self.locationManager respondsToSelector:@selector(setAllowsBackgroundLocationUpdates:)]) { [self.locationManager setAllowsBackgroundLocationUpdates:YES]; }
Fenil
2
Apa kenyamanan melakukan ini? Anda masih menguras baterai dengan memiliki akurasi tinggi yang konstan. Satu-satunya hal yang tidak Anda lakukan adalah Anda tidak benar-benar menggunakan lokasi yang sudah diterima sampai Anda mencapai interval 5 menit ...
Honey
34

Saya melakukan ini dalam aplikasi yang saya kembangkan. Penghitung waktu tidak berfungsi ketika aplikasi di latar belakang tetapi aplikasi terus menerima pembaruan lokasi. Saya membaca di suatu tempat di dokumentasi (sepertinya saya tidak dapat menemukannya sekarang, saya akan memposting pembaruan ketika saya melakukannya) bahwa suatu metode dapat dipanggil hanya pada loop run aktif ketika aplikasi di latar belakang. Delegasi aplikasi memiliki loop jalankan aktif bahkan dalam bg sehingga Anda tidak perlu membuat sendiri untuk membuat ini berfungsi. [Saya tidak yakin apakah ini penjelasan yang benar, tapi itulah bagaimana saya mengerti dari apa yang saya baca]

Pertama-tama, tambahkan locationobjek untuk kunci UIBackgroundModesdi info aplikasi Anda. Sekarang, yang perlu Anda lakukan adalah memulai pembaruan lokasi di mana saja di aplikasi Anda:

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

Selanjutnya, tulis metode untuk menangani pembaruan lokasi, katakanlah -(void)didUpdateToLocation:(CLLocation*)location, dalam delegasi aplikasi. Kemudian menerapkan metode locationManager:didUpdateLocation:fromLocationdari CLLocationManagerDelegatedalam kelas di mana Anda mulai manajer lokasi (karena kita menetapkan lokasi manager delegasi untuk 'diri'). Di dalam metode ini Anda perlu memeriksa apakah interval waktu setelah Anda menangani pembaruan lokasi telah berlalu. Anda dapat melakukan ini dengan menghemat waktu saat ini setiap saat. Jika waktu itu telah lewat, panggil metode UpdateLocation dari delegasi aplikasi Anda:

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];
        }
    }
}

Ini akan memanggil metode Anda setiap 5 menit bahkan ketika aplikasi Anda di latar belakang. Imp: Implementasi ini menguras baterai, jika keakuratan data lokasi Anda tidak kritis yang harus Anda gunakan[locationManager startMonitoringSignificantLocationChanges]

Sebelum menambahkan ini ke aplikasi Anda, silakan baca Panduan Pemrograman Kesadaran Lokasi

Bushra Shahid
sumber
3
Dengan begitu layanan lokasi selalu diaktifkan (memang menguras baterai), saya tidak mau itu. Saya ingin mengaktifkan layanan lokasi setiap n menit dan segera menonaktifkannya ketika saya memiliki perbaikan yang baik (hanya perhatikan bahwa saya tidak menjelaskan dengan jelas dalam pertanyaan saya). Saya dapat mencapai perilaku ini dalam solusi yang saya jelaskan.
wajans
3
Anda dapat mengatur keakuratan pengelola lokasi hingga 1 km - ini akan membuat baterai Anda hampir utuh. Setelah 5 menit, Anda menetapkan akurasi ke 1m. Saat Anda mendapatkan lokasi yang memuaskan (biasanya setelah 5 detik), setel akurasi kembali ke 1 km.
knagode
knagode, saya mencoba solusi yang disarankan untuk masalah baterai menguras, tetapi bahkan setelah meningkatkan akurasi setelah N menit, locationManager: metode didUpdateLocations tidak dipanggil lagi. Saya mencoba startUpdating dan stopUpdating, alih-alih menambah dan mengurangi keakuratan, disebut berhasil mendelegasikan locationManager: metode didUpdateLocations, setelah N menit, tetapi tidak berfungsi di Background MODE ...
Hardik Darji
Adapun tautan dokumentasi. Lihat di sini : "Metode objek delegasi Anda dipanggil dari utas tempat Anda memulai layanan lokasi yang sesuai. Utas itu sendiri harus memiliki loop run aktif, seperti yang ditemukan di utas utama aplikasi Anda."
Sayang
24

Sekarang iOS6 adalah cara terbaik untuk memiliki layanan lokasi yang berjalan selamanya ...

- (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]);

}

Cukup diuji seperti itu:

Saya memulai aplikasi, pergi latar belakang dan bergerak di dalam mobil beberapa menit. Kemudian saya pulang ke rumah selama 1 jam dan mulai bergerak lagi (tanpa membuka aplikasi lagi). Lokasi dimulai lagi. Kemudian berhenti selama dua jam dan mulai lagi. Semuanya baik-baik saja lagi ...

JANGAN LUPA MENGGUNAKAN layanan lokasi baru di iOS6

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

    // Lat/Lon
    float latitudeMe = loc.coordinate.latitude;
    float longitudeMe = loc.coordinate.longitude;
}
Alejandro Luengo
sumber
1
Jika aplikasi mogok atau terbunuh, sistem tidak memulai ulang, bukan?
Ken
1
Untuk kode yang lebih mudah dibaca, Anda dapat melakukan [lokasi lastObject]; alih-alih [lokasi objectAtIndex: [lokasi dihitung] - 1]
axello
metode Anda hanya di ios6?
pengwang
Apakah kita harus menggunakan ini? Saya pikir hanya memiliki kode manajer lokasi di viewDidLoad kelas, dan telah menetapkan kunci latar belakang dalam file plist bahwa register aplikasi untuk pembaruan Lokasi harus cukup baik? Bisakah Anda membantu saya dengan ini!
nithinreddy
Ya, itu akan bekerja seperti charm nithinreddy tetapi akan berhenti bekerja setelah 10 menit karena iOS membunuh utas lama setelah periode itu. Solusi saya sempurna jika Anda ingin layanan tersebut dimulai selamanya. Saya melakukan beberapa tes dua hari lalu dan menguras baterai 6% setiap jam. Menggunakan [locationManager startUpdatingLocation] sebagai gantinya akan menguras 17%
Alejandro Luengo
13

Untuk orang lain yang memiliki mimpi buruk, cari tahu yang ini. Saya punya solusi sederhana.

  1. lihat contoh ini dari raywenderlich.com -> memiliki kode sampel, ini berfungsi dengan baik, tetapi sayangnya tidak ada timer selama lokasi latar belakang. ini akan berjalan tanpa batas.
  2. Tambahkan timer dengan menggunakan:

    -(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. Hanya saja, jangan lupa menambahkan "Daftar aplikasi untuk pembaruan lokasi" di info.plist.

HelmiB
sumber
Apakah ini mendapatkan lokasi selama lebih dari 3 menit?
SleepNot
Harus mengatur lokasi dalam Kemampuan -> Mode Latar Belakang.
Sergio Andreotti
Ini tidak bekerja!! ? Saya sudah memeriksa iOS 8 dan iOS 10
Kirti
Koreksi saya jika saya salah. Berdasarkan apa yang saya baca, Anda memulai locationManager hanya sekali. Setelah itu semua interval adalah mubazir. Sejak itu sudah dimulai
Sayang
itu berfungsi tetapi dihentikan setelah 170 detik. Saya ingin menjalankan tugas saya di latar belakang untuk waktu yang tidak terbatas
Anshuman Burmman
8

Inilah yang saya gunakan:

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)
    }
}

Saya memulai pelacakan di AppDelegate seperti itu:

BackgroundLocationManager.instance.start()
hmitkov
sumber
Terima kasih. Proyek kami perlu melacak lokasi pengguna + mengirim acara PubNub di latar belakang dan solusi Anda berfungsi dengan sangat baik.
user921509
Hai Hmitkov, Di mana Saya Dapat Menghubungi metode sendLocationToServer untuk mengirim lokasi pengguna di server
AmanGupta007
@ AmanGupta007 Anda dapat memanggil sendLocationToServer di func locationManager (manajer: didUpdateLocations :). Perhatikan komentar // TODO dalam kode.
hmitkov
@hmitkov Apakah mungkin untuk memulai dan menghentikan Layanan Lokasi dengan ini saat aplikasi di latar belakang? Misalnya, mulai layanan lokasi dari pemberitahuan push ambil beberapa lat / panjang, kirim ke layanan web, kemudian berhenti memperbarui lokasi. Lakukan ini setiap kali 'konten tersedia' = 1 dimasukkan dalam push body.
DookieMan
1
Saya sudah mencoba kode ini tetapi sepertinya tidak berfungsi di iOS11? (Saya belum mengujinya pada versi lain.)
Tom
6

Sayangnya, semua asumsi Anda tampaknya benar, dan saya tidak berpikir ada cara untuk melakukan ini. Untuk menghemat masa pakai baterai, layanan lokasi iPhone didasarkan pada pergerakan. Jika ponsel duduk di satu tempat, itu tidak terlihat oleh layanan lokasi.

Hanya CLLocationManagerakan menelepon locationManager:didUpdateToLocation:fromLocation:ketika telepon menerima pembaruan lokasi, yang hanya terjadi jika salah satu dari tiga layanan lokasi (menara seluler, gps, wifi) merasakan perubahan.

Beberapa hal lain yang mungkin membantu menginformasikan solusi lebih lanjut:

  • Memulai & Menghentikan layanan menyebabkan didUpdateToLocationmetode delegasi dipanggil, tetapi newLocationmungkin memiliki cap waktu lama.

  • Pemantauan Wilayah mungkin membantu

  • Saat berjalan di latar belakang, ketahuilah bahwa mungkin sulit untuk mendapatkan dukungan LocationServices "penuh" yang disetujui oleh Apple. Dari apa yang saya lihat, mereka secara khusus dirancang startMonitoringSignificantLocationChangessebagai alternatif daya rendah untuk aplikasi yang membutuhkan dukungan lokasi latar belakang, dan sangat mendorong pengembang untuk menggunakan ini kecuali aplikasi benar-benar membutuhkannya.

Semoga berhasil!

UPDATE: Pikiran-pikiran ini mungkin sudah ketinggalan zaman sekarang. Sepertinya orang-orang sukses dengan jawaban @wans, di atas.

Chazbot
sumber
1
Ada aplikasi yang tersedia di AppStore (seperti misalnya "My Locus") yang memungkinkan untuk mendapatkan pembaruan lokasi di latar belakang. Mereka tidak menjaga layanan lokasi aktif tetapi hanya akan diaktifkan segera sesuai interval yang ditentukan. bagaimana mereka melakukan ini?
wjans
2
Dalam situasi yang Anda jelaskan, aplikasi kemungkinan besar menggunakan pendekatan startMonitoringSignificantLocationChanges. Di sini, ponsel sementara 'bangun' ketika menerima pembaruan lokasi, tetapi tidak ada interval yang dapat Anda atur untuk 'ping' layanan ini di latar belakang. Ketika ponsel bergerak (atau beralih dari Seluler ke GPS atau ke Wifi), itu memicu pembaruan. Kuliah di Stanford iTunes U tentang topik ini sangat membantu saya - semoga dapat membantu Anda menemukan solusi: itunes.apple.com/us/itunes-u/iphone-application-development/…
Chazbot
2
Terima kasih untuk pointer. Namun, saya masih tidak mengerti apa yang sedang dilakukan aplikasi ini. Bahkan jika ponsel saya ada di meja saya, tidak bergerak sama sekali, saya dapat melihat layanan lokasi dipicu setiap 10 menit (tepatnya). Jika saya mengerti benar, startMonitoringSignificantLocationChangestidak akan memberikan pembaruan dalam hal itu.
wjans
1
@wajans, bagaimana konsumsi baterai ponsel Anda, apakah Anda memperhatikannya dengan cepat mungkin karena aplikasi Mylocus?
user836026
6

Saya memang menulis aplikasi menggunakan layanan Lokasi, aplikasi harus mengirim lokasi setiap 10-an. Dan itu bekerja dengan sangat baik.

Cukup gunakan metode " allowDeferredLocationUpdatesUntilTraveled: timeout ", mengikuti dokumen Apple.

Apa yang saya lakukan adalah:

Diperlukan: Daftarkan mode latar belakang untuk memperbarui Lokasi.

1. Buat LocationMangerdan startUpdatingLocation, dengan accuracydan filteredDistanceapa pun yang Anda inginkan:

-(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. Agar aplikasi berjalan selamanya menggunakan allowDeferredLocationUpdatesUntilTraveled:timeoutmetode di latar belakang, Anda harus memulai kembali updatingLocationdengan parameter baru saat aplikasi pindah ke latar belakang, seperti ini:

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

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

3. Aplikasi mendapat updateLokasi seperti biasa dengan locationManager:didUpdateLocations:panggilan balik:

-(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. Tetapi Anda harus menangani data di kemudian locationManager:didFinishDeferredUpdatesWithError:panggilan balik untuk tujuan Anda

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

     _deferringUpdates = NO;

     //do something 
}

5. CATATAN: Saya pikir kita harus mengatur ulang parameter LocationManagersetiap kali aplikasi beralih antara mode latar / latar.

samthui7
sumber
@ Semua solusi yang dijelaskan di atas atau di atas, apakah saya lebih suka menghemat baterai perangkat atau keduanya akan berdampak sama?
Yash
2
Saya memang mencoba kedua solusi yang Anda sebutkan, dan melihat bahwa @ wjans adalah sedikit lebih menghemat baterai. Tapi, setelah kedatangan iOS 8, tampaknya solusi itu tidak berfungsi dengan baik lagi. Untuk detail lebih lanjut: sebagian besar waktu, aplikasi tidak dapat hidup lama dalam mode latar belakang.
samthui7
@ samthui7 Mengapa Anda menetapkan pausesLocationUpdatesAutomatically = false?
CedricSoubrie
Persyaratan aplikasi saya adalah untuk terus mengirim userLocation setiap 10s, sementara pausesLocationUpdatesAutomatically = truememberitahu manajer lokasi untuk menjeda pembaruan jika tampaknya tidak ada perubahan lokasi ( dokumen Apple ). Lagi pula, saya belum secara eksplisit menguji kasus location manager pauses updates: D.
samthui7
@CedricSoubrie: Pengaturan pausesLocationUpdatesAutomatically = truemenyebabkan aplikasi saya berhenti memperbarui lokasi.
samthui7
5
if ([self.locationManager respondsToSelector:@selector(setAllowsBackgroundLocationUpdates:)]) {
    [self.locationManager setAllowsBackgroundLocationUpdates:YES];
}

Ini diperlukan untuk pelacakan lokasi latar belakang sejak iOS 9.

Nilesh
sumber
1
Ini menyelamatkan hari saya! menggunakan target penyebaran iOS8, dengan perangkat iOS9
Yaro
4

Saya menggunakan metode xs2bush untuk mendapatkan interval (menggunakan timeIntervalSinceDate ) dan diperluas sedikit. Saya ingin memastikan bahwa saya mendapatkan akurasi yang diperlukan yang saya butuhkan dan juga bahwa saya tidak kehabisan baterai dengan menjaga radio gps lebih dari yang diperlukan.

Saya menjaga lokasi berjalan terus menerus dengan pengaturan berikut:

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

ini adalah daya baterai yang relatif rendah. Ketika saya siap untuk mendapatkan pembacaan lokasi berkala berikutnya, saya pertama-tama memeriksa untuk melihat apakah lokasi itu dalam akurasi yang saya inginkan, jika ya, saya kemudian menggunakan lokasi. Jika tidak, maka saya meningkatkan akurasi dengan ini:

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

dapatkan lokasi saya dan kemudian setelah saya memiliki lokasi saya mengembalikan keakuratan kembali untuk meminimalkan pembuangan baterai. Saya telah menulis sampel yang berfungsi penuh dan juga saya telah menulis sumber untuk kode sisi server untuk mengumpulkan data lokasi, menyimpannya ke database dan memungkinkan pengguna untuk melihat data gps secara real time atau mengambil dan melihat rute yang sebelumnya disimpan. Saya punya klien untuk iOS, android, windows phone dan java saya. Semua klien ditulis secara asli dan mereka semua bekerja dengan baik di latar belakang. Proyek ini berlisensi MIT.

Proyek iOS ditargetkan untuk iOS 6 menggunakan SDK dasar iOS 7. Anda bisa mendapatkan kode di sini .

Silakan ajukan masalah pada github jika Anda melihat ada masalah dengannya. Terima kasih.

Nickfox
sumber
Saya mencoba solusi Anda, tetapi tidak berfungsi ... ketika aplikasi beralih ke latar belakang, bahkan setelah meningkatkan keakuratan, aplikasi tidak dapat disebut metode didUpdateToLocation dalam kasus saya
Hardik Darji
@HardikDarji pertanyaan penting. Apakah kamu bergerak? Jika tidak, pembaruan lokasi dapat berhenti. Coba keluarkan ponsel Anda untuk berjalan-jalan atau berkendara dan lihat apakah itu memperbaikinya.
nickfox
Terima kasih atas tanggapan cepat Anda. Tapi saya ingin pembaruan lokasi setiap 2 menit, tanpa perawatan ponsel saya bergerak atau tidak. Dalam skenario ini metode didUpdateToLocation tidak dipanggil. Saya mencari di sini untuk: Bagaimana saya mendapatkan pembaruan lokasi setiap n menit !!!
Hardik Darji
coba atur timeIntervalInSeconds ke 120 dan batalkan komentar pada baris ini: locationManager.pausesLocationUpdatesAutomatically = TIDAK;
nickfox
1
apakah solusi yang Anda posting di komentar di atas membunuh masa pakai baterai, atau apakah Anda masih melakukan beberapa optimasi untuk itu?
samfr
2

Tampaknya stopUpdatingLocation adalah yang memicu timer pengawas latar belakang, jadi saya menggantinya di didUpdateLocation dengan:

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

yang tampaknya secara efektif mematikan GPS. Selektor untuk latar belakang NSTimer kemudian menjadi:

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

Yang saya lakukan adalah mengubah akurasi secara berkala untuk mendapatkan koordinat akurasi tinggi setiap beberapa menit dan karena locationManager belum dihentikan, backgroundTimeRemaining tetap pada nilai maksimumnya. Ini mengurangi konsumsi baterai dari ~ 10% per jam (dengan kCLLocationAccuracyBest konstan di latar belakang) hingga ~ 2% per jam di perangkat saya

Amit Shelgaonkar
sumber
2

Ada cocoapod APScheduledLocationManager yang memungkinkan untuk mendapatkan pembaruan lokasi latar belakang setiap n detik dengan akurasi lokasi yang diinginkan.

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

Repositori juga berisi contoh aplikasi yang ditulis dalam Swift 3.

selempang
sumber
apakah ada yang seperti ini dalam tujuan c?
Moxarth
1

Di iOS 9 dan watchOS 2.0 ada metode baru di CLLocationManager yang memungkinkan Anda meminta lokasi saat ini: CLLocationManager: requestLocation (). Ini selesai segera dan kemudian mengembalikan lokasi ke delegasi CLLocationManager.

Anda dapat menggunakan NSTimer untuk meminta lokasi setiap menit dengan metode ini sekarang dan tidak harus bekerja dengan startUpdatingLocation dan menghentikan metodeUpUpdatingLocation.

Namun jika Anda ingin mengambil lokasi berdasarkan perubahan X meter dari lokasi terakhir, cukup atur properti distanceFilter dari CLLocationManger dan ke X panggil startUpdatingLocation ().

Malcolm
sumber
-1

Terlampir adalah solusi Swift yang berbasis di:

Tentukan App registers for location updatesdalam info.plist

Jaga agar pengelola Lokasi tetap berjalan sepanjang waktu

Beralih di kCLLocationAccuracyantara BestForNavigation(selama 5 detik untuk mendapatkan lokasi) dan ThreeKilometersselama sisa periode menunggu untuk menghindari drainase baterai

Contoh ini memperbarui lokasi setiap 1 menit di Latar Depan dan setiap 15 menit di Latar Belakang.

Contoh ini berfungsi dengan baik dengan Xcode 6 Beta 6, berjalan di perangkat iOS 7.

Di App Delegate (mapView adalah Opsional menunjuk ke mapView Controller)

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
}

Di mapView (locationManager menunjuk ke objek di AppDelegate)

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")
        }
    }
eharo2
sumber