在任何时间点在 iOS7 或更高版本中在后台启动定位服务

Posted

技术标签:

【中文标题】在任何时间点在 iOS7 或更高版本中在后台启动定位服务【英文标题】:Starting Location Services In Background IN iOS7 or later at any point in time 【发布时间】:2013-10-24 19:56:10 【问题描述】:

我的算法希望这样工作: 如果我的服务器状态发生变化,我会向我的应用程序发送远程通知(其状态可能会暂停或未运行)。但是,在通知用户之前,我希望运行一些代码。在 ios 7 之前这是不可能的,但在 iOS 7 及更高版本中是可能的。 (Visit this for further details)

我希望运行的代码包括跟踪用户的位置一次然后停止。但是,跟踪位置的后台模式需要在前台开始更新位置,并且随着应用程序移动到后台,CLLocationManager Delegate 会自动获取位置更新。 但是,我只想在某个时间点跟踪一次,然后停止它。

PS:我的服务器状态可以随时改变。所以,我不能使用任何计时器或间隔

【问题讨论】:

@Wain 我希望你能帮我解决这个问题 【参考方案1】:

我找到了问题/解决方案。 当需要启动定位服务和停止后台任务时,应该延迟停止后台任务(我设置为 1 秒)。否则定位服务将无法启动。

另一个重要通知,iOS 7 中的最大后台时间现在是 3 分钟而不是 10 分钟。

也许它会对某人有所帮助。

编辑:

写了一个小例子后,我发现定位服务也应该保持开启几秒钟(在我的例子中是 3 秒钟)。在“.plist”文件中将 UIBackgroundModes 设置为“位置”。示例:

ScheduledLocationManager.h

#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>

@interface ScheduledLocationManager : NSObject <CLLocationManagerDelegate>

-(void)getUserLocationWithInterval:(int ) interval ;

@end

ScheduledLocationManager.m

#import "ScheduledLocationManager.h"

int const kMaxBGTime = 170; //3 min - 10 seconds (as bg task is killed faster)
int const kTimeToGetLocations = 3;

@implementation ScheduledLocationManager
    UIBackgroundTaskIdentifier bgTask;
    CLLocationManager *locationManager;
    NSTimer *checkLocationTimer;
    int checkLocationInterval;
    NSTimer *waitForLocationUpdatesTimer;


- (id)init

    self = [super init];
    if (self) 
        locationManager = [[CLLocationManager alloc] init];
        locationManager.delegate = self;
        locationManager.desiredAccuracy = kCLLocationAccuracyBest;
        locationManager.distanceFilter = kCLDistanceFilterNone;

        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil];
    
    return self;


-(void)getUserLocationWithInterval:(int ) interval

    checkLocationInterval = (interval > kMaxBGTime)? kMaxBGTime : interval;
    [locationManager startUpdatingLocation];


- (void)timerEvent:(NSTimer*)theTimer

    [self stopCheckLocationTimer];
    [locationManager startUpdatingLocation];

    // in iOS 7 we need to stop background task with delay, otherwise location service won't start
    [self performSelector:@selector(stopBackgroundTask) withObject:nil afterDelay:1];


-(void)startCheckLocationTimer

    [self stopCheckLocationTimer];
    checkLocationTimer = [NSTimer scheduledTimerWithTimeInterval:checkLocationInterval target:self selector:@selector(timerEvent:) userInfo:NULL repeats:NO];


-(void)stopCheckLocationTimer

    if(checkLocationTimer)
        [checkLocationTimer invalidate];
        checkLocationTimer=nil;
    


-(void)startBackgroundTask

    [self stopBackgroundTask];
    bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^
        //in case bg task is killed faster than expected, try to start Location Service
        [self timerEvent:checkLocationTimer];
    ];


-(void)stopBackgroundTask

    if(bgTask!=UIBackgroundTaskInvalid)
        [[UIApplication sharedApplication] endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    


-(void)stopWaitForLocationUpdatesTimer

    if(waitForLocationUpdatesTimer)
        [waitForLocationUpdatesTimer invalidate];
        waitForLocationUpdatesTimer =nil;
    


-(void)startWaitForLocationUpdatesTimer

    [self stopWaitForLocationUpdatesTimer];
    waitForLocationUpdatesTimer = [NSTimer scheduledTimerWithTimeInterval:kTimeToGetLocations target:self selector:@selector(waitForLoactions:) userInfo:NULL repeats:NO];


- (void)waitForLoactions:(NSTimer*)theTimer

    [self stopWaitForLocationUpdatesTimer];

    if(([[UIApplication sharedApplication ]applicationState]==UIApplicationStateBackground ||
        [[UIApplication sharedApplication ]applicationState]==UIApplicationStateInactive) &&
       bgTask==UIBackgroundTaskInvalid)
        [self startBackgroundTask];
    

    [self startCheckLocationTimer];
    [locationManager stopUpdatingLocation];


#pragma mark - CLLocationManagerDelegate methods

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
    
    if(checkLocationTimer)
        //sometimes it happens that location manager does not stop even after stopUpdationLocations
        return;
    

    //TODO: save locations

    if(waitForLocationUpdatesTimer==nil)
         [self startWaitForLocationUpdatesTimer];
    


- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error

    //TODO: handle error


#pragma mark - UIAplicatin notifications

- (void)applicationDidEnterBackground:(NSNotification *) notification

    if([self isLocationServiceAvailable]==YES)
        [self startBackgroundTask];
    


- (void)applicationDidBecomeActive:(NSNotification *) notification

    [self stopBackgroundTask];
    if([self isLocationServiceAvailable]==NO)
        //TODO: handle error
    


#pragma mark - Helpers

-(BOOL)isLocationServiceAvailable

    if([CLLocationManager locationServicesEnabled]==NO ||
       [CLLocationManager authorizationStatus]==kCLAuthorizationStatusDenied ||
       [CLLocationManager authorizationStatus]==kCLAuthorizationStatusRestricted)
        return NO;
    else
        return YES;
    



@end

您必须创建 ScheduledLocationManager 的实例并调用 getUserLocationWithInterval 传递一个以秒为单位的间隔。

【讨论】:

我目前正在审查它。如果它对我有用,我会告诉你。 @aMother 你怎么样了? @Guerrix 我很忙,无法对其进行测试。我会告诉你它是否有效。 @Vizllx 上面的例子会在后台运行一周吗?

以上是关于在任何时间点在 iOS7 或更高版本中在后台启动定位服务的主要内容,如果未能解决你的问题,请参考以下文章

在 iOS 7 或更高版本中关闭应用程序时,iOS 可以执行操作吗?

由于 tvOS 10.2 Apple TV 显示 Airplay 连接需要 iOS 7.1 或更高版本错误

如何在 java 10 或更高版本 java 11 中运行现有的 JAVA Web 启动应用程序

提交到 App Store 的应用和应用更新必须使用 Xcode 5.1.1 或更高版本以及 iOS 7 SDK 构建

来自 Android OS 8 或更高版本的连续 android 位置背景

Maven [ERROR] 不再支持源选项 5。请使用 6 或更高版本