Pembaruan lokasi latar belakang iOS secara berkala

92

Saya sedang menulis aplikasi yang membutuhkan pembaruan lokasi latar belakang dengan akurasi tinggi dan frekuensi rendah . Solusinya tampaknya menjadi tugas NSTimer latar belakang yang memulai pembaruan pengelola lokasi, yang kemudian segera dimatikan. Pertanyaan ini telah ditanyakan sebelumnya:

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

Mendapatkan lokasi pengguna setiap n menit setelah aplikasi beralih ke latar belakang

iOS Bukan masalah pengatur waktu pelacakan lokasi latar belakang biasa

Timer latar belakang iOS yang berjalan lama dengan mode latar belakang "lokasi"

Layanan latar belakang penuh waktu iOS berdasarkan pelacakan lokasi

tetapi saya belum mendapatkan contoh minimum yang berfungsi. Setelah mencoba setiap permutasi dari jawaban yang diterima di atas, saya mengumpulkan titik awal. Memasuki latar belakang:

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

dan metode delegasi:

- (void)locationManager:(CLLocationManager *)manager 
    didUpdateToLocation:(CLLocation *)newLocation 
           fromLocation:(CLLocation *)oldLocation {

    NSLog(@"%@", newLocation);

    NSLog(@"background time: %f", [UIApplication sharedApplication].backgroundTimeRemaining);
    [self.locationManager stopUpdatingLocation];

}

Perilaku saat ini adalah bahwa backgroundTimeRemainingpenurunan dari 180 detik ke nol (saat pencatatan lokasi), dan kemudian pengendali kedaluwarsa dijalankan dan tidak ada pembaruan lokasi lebih lanjut yang dibuat. Bagaimana cara mengubah kode di atas untuk menerima pembaruan lokasi berkala di latar belakang tanpa batas waktu ?

Pembaruan : Saya menargetkan iOS 7 dan tampaknya ada beberapa bukti bahwa tugas latar belakang berperilaku berbeda:

Mulai Manajer Lokasi di iOS 7 dari tugas latar belakang

pcoving
sumber
Anda tidak menyebutkan versi iOS yang Anda targetkan. iOS 7 (misalnya) memiliki sistem multitasking yang berbeda dari pendahulunya.
Jason Whitehorn
@Jamur_kejang Bagus. Saya telah memperbarui pertanyaan tersebut.
pcoving
2
@pcoving: Akankah solusi Anda berfungsi meskipun aplikasi dimatikan atau dihentikan?
Nirav

Jawaban:

62

Tampaknya itulah stopUpdatingLocationyang memicu pengatur waktu pengawas latar belakang, jadi saya menggantinya didUpdateLocationdengan:

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

yang tampaknya mematikan GPS secara efektif. Pemilih untuk latar belakang NSTimerkemudian 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 locationManagerbelum dihentikan, backgroundTimeRemainingtetap pada nilai maksimumnya. Ini mengurangi konsumsi baterai dari ~ 10% per jam (dengan kCLLocationAccuracyBestlatar belakang konstan ) menjadi ~ 2% per jam di perangkat saya.

pcoving
sumber
4
Bisakah Anda memperbarui pertanyaan Anda dengan contoh yang berfungsi penuh? Sesuatu seperti: "PEMBARUAN: Saya menemukan
jawabannya
@Virussmca Saya kembali ke jawaban sebelumnya yang jauh lebih kuat dengan kinerja serupa. Menghentikan pembaruan lokasi sama sekali tampaknya memicu kondisi balapan (atau skenario non-deterministik lainnya).
pcoving
Pendekatan ini tidak akan bekerja seperti yang Anda harapkan: 1) Meningkatkan nilai properti distanceFilter tidak akan menghemat baterai karena gps masih harus terus mencari lokasi Anda. 2) Di iOS 7, waktu latar belakang tidak bersebelahan. 3) Anda mungkin mempertimbangkan layanan signifikanChangeLocation. 4) Terlepas dari soution Anda di iOS 7, Anda tidak dapat memulai UIBackgroundModes - lokasi dari latar belakang ...
2013 lainnya
2
@aMother 1) Saya menyadari bahwa meningkatkan filter jarak (dan tidak memproses panggilan balik) tidak menghemat banyak baterai. Namun, setDesiredAccuracy melakukannya! Itu jatuh kembali pada informasi menara seluler yang pada dasarnya datang gratis. 2) Tidak bersebelahan? 3) Saya telah menetapkan, dengan huruf tebal, bahwa saya memerlukan akurasi tinggi. Perubahan lokasi yang signifikan tidak menyediakan ini. 4) Layanan lokasi tidak dimulai / dihentikan dari latar belakang.
pcoving
2
Saya tahu ini adalah posting lama sekarang, tetapi pengguna aplikasi saya yang menggunakan trik ini telah memperhatikan peningkatan yang signifikan dalam penggunaan baterai sejak meningkatkan ke iOS9. Saya belum menyelidikinya, tetapi saya merasa 'trik' ini mungkin tidak lagi berfungsi?
Gary Wright
45

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

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

Langkah-langkahnya adalah sebagai berikut:

Diperlukan: Daftarkan mode latar belakang untuk memperbarui Lokasi.

1. Buat LocationManger dan startUpdatingLocation, dengan akurasi dan filteredDistance sesuai keinginan Anda:

-(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 tetap berjalan selamanya menggunakan metode "allowDeferredLocationUpdatesUntilTraveled: timeout" di latar belakang, Anda harus memulai ulang updateLocation dengan parameter baru saat aplikasi berpindah 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 mendapatkan updateLocations seperti biasa dengan callback "locationManager: didUpdateLocations:":

-(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. Namun Anda harus menangani data di kemudian "locationManager: didFinishDeferredUpdatesWithError:" callback untuk tujuan Anda

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

     _deferringUpdates = NO;

     //do something 
}

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

samthui7
sumber
Ini bekerja dengan baik dan saya merasa ini adalah cara yang tepat untuk melakukan seperti yang disebutkan dalam WWDC tahun lalu !! Terima kasih atas posnya
prasad1250
@ samthui7 terima kasih ini berfungsi, tetapi saya juga perlu mendapatkan lokasi setelah penghentian. bagaimana bisa.??
Mohit Tomar
@ samthui7 .. silakan lihat antrean ini ... stackoverflow.com/questions/37338466/…
Suraj Sukale
2
@amar: Saya juga memeriksa di iOS 10 beta, masih berfungsi. Perhatikan bahwa kita perlu menambahkan beberapa kode sebelum startUpdatingLocation: allowsBackgroundLocationUpdates = YESdan requestAlwaysAuthorizationatau requestWhenInUseAuthorization. Misalnya: `CLLocationManager * locationManager = [[CLLocationManager alokasi] init]; locationManager.delegate = self; locationManager.allowsBackgroundLocationUpdates = YA; [locationManager requestAlwaysAuthorization]; [locationManager startUpdatingLocation]; `
samthui7
1
@Nirav Tidak. Saya rasa Apple tidak akan membiarkan kami melakukan itu, sejauh yang saya tahu.
samthui7
38
  1. Jika Anda memiliki UIBackgroundModesdi plist Anda dengan locationkunci maka Anda tidak perlu menggunakan beginBackgroundTaskWithExpirationHandlermetode. Itu berlebihan. Juga Anda menggunakannya secara tidak benar (lihat di sini ) tetapi itu diperdebatkan karena plist Anda diatur.

  2. Dengan UIBackgroundModes locationdi plist aplikasi akan terus berjalan di latar belakang tanpa batas waktu hanya selama CLLocationMangerberjalan. Jika Anda menelepon stopUpdatingLocationsaat berada di latar belakang maka aplikasi akan berhenti dan tidak akan mulai lagi.

    Mungkin Anda dapat menelepon beginBackgroundTaskWithExpirationHandlersebelum menelepon stopUpdatingLocationdan setelah menelepon startUpdatingLocationAnda dapat memanggilnya endBackgroundTaskagar tetap di latar belakang saat GPS dihentikan, tetapi saya tidak pernah mencobanya - ini hanya sebuah ide.

    Opsi lain (yang belum saya coba) adalah menjaga pengelola lokasi tetap berjalan saat berada di latar belakang tetapi setelah Anda mendapatkan lokasi yang akurat, ubah desiredAccuracyproperti menjadi 1000m atau lebih tinggi untuk memungkinkan chip GPS dimatikan (untuk menghemat baterai). Kemudian 10 menit kemudian ketika Anda membutuhkan pembaruan lokasi lainnya, ubah desiredAccuracykembali ke 100m untuk mengaktifkan GPS sampai Anda mendapatkan lokasi yang akurat, ulangi.

  3. Saat Anda memanggil startUpdatingLocationmanajer lokasi, Anda harus memberinya waktu untuk mendapatkan posisi. Anda tidak harus segera menelepon stopUpdatingLocation. Kami membiarkannya berjalan maksimal 10 detik atau sampai kami mendapatkan lokasi akurasi tinggi yang tidak di-cache.

  4. Anda perlu memfilter lokasi cache dan memeriksa keakuratan lokasi yang Anda dapatkan untuk memastikannya memenuhi keakuratan minimum yang diperlukan (lihat di sini ). Pembaruan pertama yang Anda dapatkan mungkin berumur 10 menit atau 10 hari. Akurasi pertama yang Anda dapatkan mungkin 3000m.

  5. Pertimbangkan untuk menggunakan API perubahan lokasi yang signifikan sebagai gantinya. Setelah Anda mendapatkan pemberitahuan perubahan signifikan, Anda dapat memulai CLLocationManagerselama beberapa detik untuk mendapatkan posisi akurasi tinggi. Saya tidak yakin, saya tidak pernah menggunakan layanan perubahan lokasi yang signifikan.

progrmr
sumber
CoreLocationReferensi Apple tampaknya merekomendasikan penggunaan beginBackgroundTaskWithExpirationHandlersaat memproses data lokasi di latar belakang: "Jika aplikasi iOS membutuhkan lebih banyak waktu untuk memproses data lokasi, ia dapat meminta lebih banyak waktu eksekusi latar belakang menggunakan metode beginBackgroundTaskWithName: expirationHandler: kelas UIApplication." - developer.apple.com/library/ios/documentation/UserExperience/…
eliocs
4
jangan lupa menambahkan _locationManager.allowsBackgroundLocationUpdates = YES; untuk iOS9 +, jika tidak, aplikasi akan tidur setelah 180 detik
kolyancz
@kolyancz Saya baru saja mencobanya di iOS 10.1.1 untuk iPhone 6+. Butuh sekitar ~ 17 menit untuk tidur dan memicu locationManagerDidPauseLocationUpdates. Saya memverifikasi perilaku itu 10 kali!
Madu
Saya benar-benar tidak mengerti mengapa Anda mengatakan itu diperdebatkan dan kemudian Anda mengatakan Anda harus membungkusnya di dalam backgroundTask ...
Honey
10

Jika sudah waktunya untuk memulai layanan lokasi dan menghentikan tugas latar belakang, tugas latar belakang harus dihentikan dengan penundaan (1 detik sudah cukup). Jika tidak, layanan lokasi tidak akan dimulai. Juga Layanan Lokasi harus dibiarkan ON selama beberapa detik (misalnya 3 detik).

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 aplikasi contoh yang ditulis di Swift 3.

selempang
sumber
1

Bagaimana kalau mencobanya dengan startMonitoringSignificantLocationChanges:API? Ini pasti menembak dengan frekuensi yang lebih sedikit dan akurasinya cukup baik. Selain itu, ia memiliki lebih banyak keuntungan daripada menggunakan locationManagerAPI lain .

Lebih banyak tentang API ini telah dibahas di tautan ini

thatzprem.dll
sumber
1
Saya sudah mencoba pendekatan ini. Ini melanggar persyaratan akurasi - dari dokumen : This interface delivers new events only when it detects changes to the device’s associated cell towers
pcoving
1

Anda perlu menambahkan mode pembaruan lokasi di aplikasi Anda dengan menambahkan kunci berikut di info.plist Anda.

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

didUpdateToLocationmetode akan dipanggil (bahkan saat aplikasi Anda berada di latar belakang). Anda dapat melakukan apa saja atas dasar pemanggilan metode ini

aahsanali
sumber
Mode pembaruan lokasi sudah ada di info.plist. Seperti yang saya sebutkan, saya mendapatkan pembaruan hingga batas waktu latar belakang 180 detik.
pcoving
0

Setelah iOS 8, ada beberapa perubahan dalam kerangka kerja CoreLocation terkait pengambilan latar belakang dan pembaruan yang dibuat oleh apple.

1) Apple memperkenalkan permintaan AlwaysAuthorization untuk mengambil pembaruan lokasi di aplikasi.

2) Apple memperkenalkan backgroundLocationupdates untuk mengambil lokasi dari latar belakang di iOS.

Untuk mengambil lokasi di latar belakang, Anda perlu mengaktifkan Pembaruan Lokasi dalam Kemampuan 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
    }

}
Tech
sumber
0

Untuk menggunakan layanan lokasi standar saat aplikasi berada di latar belakang, pertama-tama Anda perlu mengaktifkan Mode Latar Belakang di tab Kapabilitas pada pengaturan Target, dan pilih Pembaruan lokasi.

Atau, tambahkan langsung ke Info.plist .

<key>NSLocationAlwaysUsageDescription</key>
 <string>I want to get your location Information in background</string>
<key>UIBackgroundModes</key>
 <array><string>location</string> </array>

Kemudian Anda perlu menyiapkan CLLocationManager

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

Cepat

self.locationManager.delegate = self
if #available (iOS 8.0,*) {
    self.locationManager.requestAlwaysAuthorization()
}
if #available (iOS 9.0,*) {
    self.locationManager.allowsBackgroundLocationUpdates = true
}
self.locationManager.startUpdatingLocation()

Ref: https://medium.com/@javedmultani16/location-service-in-the-background-ios-942c89cd99ba

Tuan Javed Multani
sumber
-2
locationManager.allowsBackgroundLocationUpdates = true
Papon Smc
sumber
4
Bisakah Anda mengedit jawaban Anda dan menambahkan beberapa penjelasan mengapa / bagaimana ini menyelesaikan masalah?
barbsan