后台长时间定时定位-Location实践经验

前言

最近接到这样一个需求,每隔固定时间采集用户的位置,然后再把这些数据上传到服务器。研究了下ios的定位功能,在后台定时遇到了一些困难。当app进入后台状态,定时器就不再运行,导致无法取到用户的位置。
在网上查了一些资料,发现有人已经实现了这个功能,它是一个Github上的第三方库,叫Location,不仅能在后台定时采集位置数据,还优化了定位方式,减少耗电。
接下来我们来看看Location是如何实现的以及它存在的问题。

代码结构


Location代码结构
BackgroundTaskManager

负责创建、管理后台任务,提供两个方法,开始和结束后台任务。它实现了后台任务的无限期运行。
@interface BackgroundTaskManager : NSObject

+(instancetype)sharedBackgroundTaskManager;

-(UIBackgroundTaskIdentifier)beginNewBackgroundTask;
-(void)endAllBackgroundTasks;

@end
LocationShareModel

包装了后台任务类,也管理定位定时器。

@interface LocationShareModel : NSObject
@property (nonatomic) NSTimer *timer;
@property (nonatomic) NSTimer * delay10Seconds;
@property (nonatomic) BackgroundTaskManager * bgTask;
@property (nonatomic) NSMutableArray *myLocationArray;
+(id)sharedModel;
@end
LocationTracker

负责管理后台定时定位的主类。
我们先看一下它提供的接口。

+ (CLLocationManager *)sharedLocationManager;

//开始追踪定位
- (BOOL)startLocationTracking;
//停止追踪定位
- (void)stopLocationTracking;
//向服务器发送已获取的设备位置数据
- (void)updateLocationToServer;

LocationTracker类的初始化

+ (CLLocationManager *)sharedLocationManager {
    static CLLocationManager *_locationManager;
    @synchronized(self) {
        if (_locationManager == nil) {
            _locationManager = [[CLLocationManager alloc] init];
            //设备位置精度,这里设置为最高精度
            _locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
            //是否暂停更新
            _locationManager.pausesLocationUpdatesAutomatically = NO;             
            //iOS9的新特性,打开后台位置更新,还要在plist中配置
            if (IOS9ORLATER) {
                _locationManager.allowsBackgroundLocationUpdates = YES;
            }
        }
    }
    return _locationManager;
}

当应用进入后台,借助BackgroundTaskManager无限延长后台任务的存活时间,进行后台采集位置信息。

- (id)init {
    if (self==[super init]) {
        //Get the share model and also initialize myLocationArray
        self.shareModel = [LocationShareModel sharedModel];
        self.shareModel.myLocationArray = [[NSMutableArray alloc]init];

        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationEnterBackground) name:UIApplicationDidEnterBackgroundNotification object:nil];
    }
    return self;
}

-(void)applicationEnterBackground{
    CLLocationManager *locationManager = [LocationTracker sharedLocationManager];
    locationManager.delegate = self;
    locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
    locationManager.distanceFilter = kCLDistanceFilterNone;

    if(IOS8ORLATER) {
        [locationManager requestAlwaysAuthorization];
    }
    [locationManager startUpdatingLocation];

    //Use the BackgroundTaskManager to manage all the background Task
    self.shareModel.bgTask = [BackgroundTaskManager sharedBackgroundTaskManager];
    [self.shareModel.bgTask beginNewBackgroundTask];
}

持续获取设备的位置,是个特别耗电的任务。为了减少耗电,定时关闭位置服务。在接收到位置信息10秒后,关闭位置服务,一分钟后再重新打开位置服务,这样就达到减少耗电的目的。我们看代码的实现

-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{

    ......

    self.shareModel.bgTask = [BackgroundTaskManager sharedBackgroundTaskManager];
    [self.shareModel.bgTask beginNewBackgroundTask];

    //Restart the locationMaanger after 1 minute
    self.shareModel.timer = [NSTimer scheduledTimerWithTimeInterval:60 target:self
                                                           selector:@selector(restartLocationUpdates)
                                                           userInfo:nil
                                                            repeats:NO];

    //Will only stop the locationManager after 10 seconds, so that we can get some accurate locations
    //The location manager will only operate for 10 seconds to save battery
    if (self.shareModel.delay10Seconds) {
        [self.shareModel.delay10Seconds invalidate];
        self.shareModel.delay10Seconds = nil;
    }

    self.shareModel.delay10Seconds = [NSTimer scheduledTimerWithTimeInterval:10 target:self
                                                                    selector:@selector(stopLocationDelayBy10Seconds)
                                                                    userInfo:nil
                                                                     repeats:NO];

}

重启位置服务

- (void) restartLocationUpdates
{
    if (self.shareModel.timer) {
        [self.shareModel.timer invalidate];
        self.shareModel.timer = nil;
    }

    CLLocationManager *locationManager = [LocationTracker sharedLocationManager];
    locationManager.delegate = self;
    locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
    locationManager.distanceFilter = kCLDistanceFilterNone;

    if(IOS8ORLATER) {
        [locationManager requestAlwaysAuthorization];
    }
    [locationManager startUpdatingLocation];
}

按照Location库的设计,在updateLocationToServer方法中,处理向服务器发送信息。

- (void)updateLocationToServer {

    .........

    //TODO: 在这里插入你的代码,处理向服务器发送位置信息


    ........

}

实践

这个类库解决了后台定时定位的问题,使用也很简单,而且还处理了定位耗电的问题,使耗电量降低到每小时5%左右。但在实践的过程中发现它存在一些问题。

  1. 发送数据到服务器方法的定时器,最大支持3分钟;
  2. 发送数据到服务器方法的定时器,在运行2小时后就停止了;
  3. 发送数据到服务器方法的定时器,不准确;

经过一些尝试,废弃了updateLocationToServer方法,将发送数据的代码移到locationManager中,才解决了这几个问题。具体解决方法如下:
首先为LocationTracker类添加一个私有的属性,记录上次执行发送数据的时间。

@interface LocationTracker ()
@property (nonatomic, strong) NSDate *lastSendDate;
@end

在locationManager回调方法中添加自己的代码。

    .......
   //Select only valid location and also location with good accuracy
        if(newLocation!=nil&&theAccuracy>0
           &&theAccuracy<2000
           &&(!(theLocation.latitude==0.0&&theLocation.longitude==0.0))){

            self.myLastLocation = theLocation;
            self.myLastLocationAccuracy= theAccuracy;

            NSMutableDictionary * dict = [[NSMutableDictionary alloc]init];
            [dict setObject:[NSNumber numberWithFloat:theLocation.latitude] forKey:@"latitude"];
            [dict setObject:[NSNumber numberWithFloat:theLocation.longitude] forKey:@"longitude"];
            [dict setObject:[NSNumber numberWithFloat:theAccuracy] forKey:@"theAccuracy"];

            //Add the vallid location with good accuracy into an array
            //Every 1 minute, I will select the best location based on accuracy and send to server
            [self.shareModel.myLocationArray addObject:dict];

            //这里插入代码,处理发送位置数据到服务器
                if (!self.lastSendDate || [[self.lastSendDate dateByAddingHours:1] compare:[NSDate date]] <= 0) {
                    self.lastSendDate = [NSDate date];
                    }
            }


作者:潇潇潇潇潇潇潇
链接:http://www.jianshu.com/p/295a3ae5f8a6
來源:简书

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

转载自:http://www.jianshu.com/p/7ccc0860bdbd

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值