Cập nhật vị trí nền iOS định kỳ


91

Tôi đang viết một ứng dụng yêu cầu cập nhật vị trí nền với độ chính xác cao và tần suất thấp . Giải pháp dường như là một tác vụ NSTimer nền bắt đầu cập nhật của trình quản lý vị trí, sau đó ngay lập tức tắt. Câu hỏi này đã được hỏi trước đây:

Làm cách nào để nhận bản cập nhật vị trí nền sau mỗi n phút trong ứng dụng iOS của tôi?

Nhận vị trí của người dùng sau mỗi n phút sau khi ứng dụng chuyển sang chế độ nền

iOS Không phải là vấn đề điển hình của bộ hẹn giờ theo dõi vị trí nền

Bộ hẹn giờ chạy nền lâu dài trên iOS với chế độ nền "vị trí"

Dịch vụ nền toàn thời gian của iOS dựa trên theo dõi vị trí

nhưng tôi vẫn chưa nhận được một ví dụ tối thiểu hoạt động. Sau khi thử mọi hoán vị của các câu trả lời được chấp nhận ở trên, tôi đặt lại một điểm bắt đầu. Nhập nền:

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

và phương thức ủy quyền:

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

    NSLog(@"%@", newLocation);

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

}

Hành vi hiện tại là backgroundTimeRemaininggiảm từ 180 giây xuống 0 (trong khi ghi lại vị trí), và sau đó trình xử lý hết hạn thực thi và không có cập nhật vị trí nào được tạo thêm. Làm cách nào để sửa đổi mã trên để nhận được cập nhật vị trí định kỳ trong nền vô thời hạn ?

Cập nhật : Tôi đang nhắm mục tiêu iOS 7 và dường như có một số bằng chứng cho thấy các tác vụ nền hoạt động khác nhau:

Khởi động Trình quản lý vị trí trong iOS 7 từ tác vụ nền


Bạn không đề cập đến phiên bản iOS mà bạn đang nhắm mục tiêu. iOS 7 (chẳng hạn) có hệ thống đa nhiệm khác với phiên bản tiền nhiệm.
Jason Whitehorn

@JasonWhitehorn Điểm tốt. Tôi đã cập nhật câu hỏi.
pcoving

2
@pcoving: Liệu giải pháp của bạn có hoạt động ngay cả khi ứng dụng bị giết hoặc bị chấm dứt không?
Nirav,

Câu trả lời:


62

Có vẻ như đó stopUpdatingLocationlà thứ kích hoạt bộ hẹn giờ cơ quan giám sát nền, vì vậy tôi đã thay thế nó didUpdateLocationbằng:

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

điều này dường như làm tắt GPS một cách hiệu quả. Bộ chọn cho nền NSTimersau đó trở thành:

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

Tất cả những gì tôi đang làm là chuyển đổi định kỳ độ chính xác để có được một tọa độ chính xác cao sau mỗi vài phút và bởi vì tọa độ locationManagerchưa bị dừng nên backgroundTimeRemainingvẫn ở giá trị lớn nhất. Điều này làm giảm mức tiêu thụ pin từ ~ 10% mỗi giờ (với không đổi kCLLocationAccuracyBesttrong nền) xuống còn ~ 2% mỗi giờ trên thiết bị của tôi.


4
Bạn có thể cập nhật câu hỏi của mình với ví dụ làm việc đầy đủ không? Một cái gì đó như: "UPDATE: I figured it out Đây là giải pháp của tôi ...."
smholloway

@Virussmca Tôi đã hoàn nguyên về câu trả lời trước đó mạnh mẽ hơn nhiều với hiệu suất tương tự. Việc dừng cập nhật vị trí hoàn toàn dường như sẽ kích hoạt tình trạng cuộc đua (hoặc một số trường hợp không xác định khác).
pcoving

Cách tiếp cận này sẽ không hoạt động như bạn mong đợi: 1) Tăng giá trị của thuộc tính distanceFilter không dẫn đến việc tiết kiệm pin vì gps vẫn phải tiếp tục tìm vị trí của bạn. 2) Trong iOS 7, thời gian nền không liên tục. 3) Bạn có thể tính đến các dịch vụ quan trọng của ChangeLocation. 4) Ngoài sự thận trọng của bạn trong iOS 7, bạn không thể khởi động UIBackgroundModes - vị trí từ nền ...
aMother

2
@aMother 1) Tôi nhận thấy rằng việc tăng bộ lọc khoảng cách (và không xử lý các cuộc gọi lại) không tiết kiệm nhiều pin. Tuy nhiên, setDesnticAccuracy thì không! Nó dựa trên thông tin tháp di động về cơ bản là miễn phí. 2) Không liền kề? 3) Tôi đã nêu rõ, in đậm, rằng tôi yêu cầu độ chính xác cao. Những thay đổi đáng kể về vị trí không cung cấp điều này. 4) Dịch vụ định vị không được khởi động / dừng ở chế độ nền.
pcoving

2
Tôi biết đây là một bài viết cũ, nhưng người dùng ứng dụng của tôi sử dụng thủ thuật này đã nhận thấy mức sử dụng pin đã tăng lên đáng kể kể từ khi nâng cấp lên iOS9. Tôi chưa tìm hiểu kỹ, nhưng tôi có cảm giác rằng 'thủ thuật' này có thể không còn tác dụng nữa?
Gary Wright

45

Tôi đã viết một ứng dụng bằng dịch vụ Vị trí, ứng dụng phải gửi vị trí cứ sau 10 giây. Và nó hoạt động rất tốt.

Chỉ cần sử dụng phương pháp " allowDeferredLocationUpdatesUntilTraveled: timeout ", theo tài liệu của Apple.

Các bước thực hiện như sau:

Yêu cầu: Đăng ký chế độ nền để cập nhật Vị trí.

1. Tạo LocationManger và startUpdatingLocation, với độ chính xác và LọcDistance như bất cứ điều gì bạn muốn:

-(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. Để giữ cho ứng dụng chạy mãi mãi bằng cách sử dụng phương thức "allowDeferredLocationUpdatesUntilTraveled: timeout" trong nền, bạn phải khởi động lại updateLocation với tham số mới khi ứng dụng chuyển sang chế độ nền, như sau:

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

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

3. Ứng dụng được cập nhậtLocations như bình thường với 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. Nhưng bạn nên xử lý dữ liệu trong lệnh gọi lại "locationManager: didFinishDeferredUpdatesWithError:" cho mục đích của bạn

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

     _deferringUpdates = NO;

     //do something 
}

5. LƯU Ý: Tôi nghĩ chúng ta nên đặt lại các thông số của LocationManager mỗi khi ứng dụng chuyển đổi giữa chế độ nền / nền.


Điều này hoạt động tuyệt vời và tôi cảm thấy đây là cách đúng đắn để làm như đã đề cập trong WWDC năm ngoái !! Cảm ơn vì bài đăng
prasad1250

@ samthui7 cảm ơn cái này hoạt động tốt, nhưng tôi cũng cần tìm vị trí sau khi chấm dứt. làm thế nào nó có thể được. ??
Mohit Tomar

@ samthui7 .. vui lòng xem hàng đợi này ... stackoverflow.com/questions/37338466/…
Suraj Sukale

2
@amar: Tôi cũng đã kiểm tra trên iOS 10 beta, nó vẫn hoạt động. Lưu ý rằng chúng ta cần thêm một số mã trước startUpdatingLocation: allowsBackgroundLocationUpdates = YESrequestAlwaysAuthorizationhoặc requestWhenInUseAuthorization. Vd: `CLLocationManager * locationManager = [[CLLocationManager CẤP] init]; locationManager.delegate = self; locationManager.allowsBackgroundLocationUpdates = CÓ; [locationManager requestAlwaysAuthorization]; [locationManager startUpdatingLocation]; `
samthui7

1
@Nirav Không. Tôi không nghĩ Apple có thể để chúng tôi làm điều đó, theo như tôi biết.
samthui7

38
  1. Nếu bạn có UIBackgroundModestrong plist của bạn với locationchìa khóa thì bạn không cần sử dụng beginBackgroundTaskWithExpirationHandlerphương pháp này. Đó là thừa. Ngoài ra, bạn đang sử dụng nó không đúng cách (xem tại đây ) nhưng đó là tranh cãi vì plist của bạn đã được thiết lập.

  2. Với UIBackgroundModes locationtrong plist, ứng dụng sẽ tiếp tục chạy trong nền vô thời hạn miễn CLLocationMangerlà đang chạy. Nếu bạn gọi stopUpdatingLocationtrong nền thì ứng dụng sẽ dừng và không bắt đầu lại.

    Có thể bạn có thể gọi beginBackgroundTaskWithExpirationHandlerngay trước khi gọi stopUpdatingLocationvà sau khi gọi, startUpdatingLocationbạn có thể gọi endBackgroundTaskđể giữ nó ở chế độ nền trong khi GPS bị dừng, nhưng tôi chưa bao giờ thử điều đó - đó chỉ là một ý tưởng.

    Một tùy chọn khác (mà tôi chưa thử) là giữ cho trình quản lý vị trí chạy trong nền nhưng khi bạn có được vị trí chính xác, hãy thay đổi thuộc desiredAccuracytính thành 1000m hoặc cao hơn để cho phép tắt chip GPS (để tiết kiệm pin). Sau đó 10 phút khi bạn cần cập nhật vị trí khác, hãy thay đổi desiredAccuracytrở lại 100m để bật GPS cho đến khi bạn có được vị trí chính xác, lặp lại.

  3. Khi bạn gọi startUpdatingLocationcho người quản lý vị trí, bạn phải cho nó thời gian để có được một vị trí. Bạn không nên gọi ngay lập tức stopUpdatingLocation. Chúng tôi để nó chạy trong tối đa 10 giây hoặc cho đến khi chúng tôi nhận được vị trí có độ chính xác cao không được lưu trong bộ nhớ cache.

  4. Bạn cần lọc ra các vị trí được lưu trong bộ nhớ cache và kiểm tra độ chính xác của vị trí bạn nhận được để đảm bảo rằng vị trí đó đáp ứng độ chính xác cần thiết tối thiểu của bạn (xem tại đây ). Bản cập nhật đầu tiên bạn nhận được có thể sau 10 phút hoặc 10 ngày. Độ chính xác đầu tiên bạn nhận được có thể là 3000m.

  5. Thay vào đó, hãy cân nhắc sử dụng các API thay đổi vị trí quan trọng. Sau khi nhận được thông báo thay đổi quan trọng, bạn có thể bắt đầu CLLocationManagertrong vài giây để có được vị trí chính xác cao. Tôi không chắc chắn, tôi chưa bao giờ sử dụng các dịch vụ thay đổi vị trí quan trọng.


CoreLocationTham chiếu của Apple dường như khuyên bạn nên sử dụng beginBackgroundTaskWithExpirationHandlerkhi xử lý dữ liệu vị trí trong nền: "Nếu một ứng dụng iOS cần thêm thời gian để xử lý dữ liệu vị trí, thì ứng dụng đó có thể yêu cầu thêm thời gian thực thi trong nền bằng phương thức beginBackgroundTaskWithName: expirationHandler: của lớp UIApplication." - developer.apple.com/library/ios/documentation/UserExperience/…
eliocs

4
đừng quên thêm _locationManager.allowsBackgroundLocationUpdates = YES; cho iOS9 +, nếu không ứng dụng sẽ đi ngủ sau 180 giây
kolyancz

@kolyancz Tôi vừa dùng thử trên iOS 10.1.1 cho iPhone 6+. Mất khoảng ~ 17 phút để nó chuyển sang chế độ ngủ và kích hoạt locationManagerDidPauseLocationUpdates. Tôi đã xác minh hành vi đó 10 lần!
Honey,

Tôi thật sự không hiểu tại sao bạn đang nói nó tranh luận và sau đó bạn đang nói bạn nên quấn nó bên trong một backgroundTask ...
Mật ong

10

Khi đến lúc bắt đầu dịch vụ vị trí và dừng tác vụ nền, tác vụ nền phải dừng lại với độ trễ (1 giây là đủ). Nếu không, dịch vụ vị trí sẽ không bắt đầu. Ngoài ra Dịch vụ vị trí nên được BẬT trong vài giây (ví dụ: 3 giây).

Có một cocoapod APSchedisedLocationManager cho phép cập nhật vị trí nền sau mỗi n giây với độ chính xác vị trí mong muốn.

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

Kho cũng chứa một ứng dụng mẫu được viết bằng Swift 3.


1

Làm thế nào về việc thử với startMonitoringSignificantLocationChanges:API? Nó chắc chắn bắn với tần số ít hơn và độ chính xác là khá tốt. Ngoài ra, nó có nhiều lợi thế hơn so với sử dụng các locationManagerAPI khác .

Nhiều thông tin khác về API này đã được thảo luận trên liên kết này


1
Tôi đã thử cách tiếp cận này. Nó vi phạm các yêu cầu độ chính xác - từ tài liệu : This interface delivers new events only when it detects changes to the device’s associated cell towers
pcoving

1

Bạn cần thêm chế độ cập nhật vị trí trong ứng dụng của mình bằng cách thêm khóa sau vào bạn info.plist.

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

didUpdateToLocationphương thức sẽ được gọi (ngay cả khi ứng dụng của bạn ở chế độ nền). Bạn có thể thực hiện bất kỳ điều gì trên cơ sở của lệnh gọi phương thức này


Chế độ cập nhật vị trí đã có trong info.plist. Như tôi đã đề cập, tôi sẽ nhận được bản cập nhật cho đến khi thời gian chờ nền là 180 giây.
pcoving 27/09/13

0

Sau iOS 8, họ có một số thay đổi trong việc tìm nạp và cập nhật nền liên quan đến khung CoreLocation do apple thực hiện.

1) Apple giới thiệu yêu cầu AlwaysAuthorization để tìm nạp bản cập nhật vị trí trong ứng dụng.

2) Apple giới thiệu backgroundLocationupdates để tìm nạp vị trí từ nền trong iOS.

Để tìm nạp vị trí trong nền, bạn cần bật Cập nhật vị trí trong Khả năng của Xcode.

//
//  ViewController.swift
//  DemoStackLocation
//
//  Created by iOS Test User on 02/01/18.
//  Copyright © 2018 iOS Test User. All rights reserved.
//

import UIKit
import CoreLocation

class ViewController: UIViewController {

    var locationManager: CLLocationManager = CLLocationManager()
    override func viewDidLoad() {
        super.viewDidLoad()
        startLocationManager()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    internal func startLocationManager(){
        locationManager.requestAlwaysAuthorization()
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        locationManager.delegate = self
        locationManager.allowsBackgroundLocationUpdates = true
        locationManager.startUpdatingLocation()
    }

}

extension ViewController : CLLocationManagerDelegate {

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]){
        let location = locations[0] as CLLocation
        print(location.coordinate.latitude) // prints user's latitude
        print(location.coordinate.longitude) //will print user's longitude
        print(location.speed) //will print user's speed
    }

}

0

Để sử dụng dịch vụ vị trí tiêu chuẩn trong khi ứng dụng ở chế độ nền, trước tiên bạn cần bật Chế độ nền trong tab Khả năng của cài đặt Mục tiêu và chọn Cập nhật vị trí.

Hoặc thêm nó trực tiếp vào Info.plist .

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

Sau đó, bạn cần thiết lập CLLocationManager

Mục tiêu 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];

Nhanh

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

Tham khảo: https://medium.com/@javedmultani16/location-service-in-the-background-ios-942c89cd99ba


-2
locationManager.allowsBackgroundLocationUpdates = true

4
Bạn có thể chỉnh sửa câu trả lời của mình và thêm một số giải thích tại sao / cách điều này giải quyết vấn đề không?
barbsan
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.