当前位置: 首页 > 科技观察

后台定位上传的代码实践_0

时间:2023-03-17 01:11:02 科技观察

上一篇文章提到我现在做的是一个基于LBS定位的社交APP。其中一个主要功能就是能够实时定位到每个成员在社交圈中的位置。后台实时上传位置是一个很重要的技术点。接下来,我们就来说说吧。我需要这方面的实践经验。首先我们来看一下实现该功能的具体要求。由于我们是一个实时定位的生活社交APP,所以我们需要做到以下几点:一次上报由于我们希望能够在APP中实时反馈用户的位置变化,所以定时上报是刚需。2.如果用户移动速度很慢,一定距离报告一次。移动速度大约为1m/s)这时候如果还是按照(1)中的方法上报,地图上的点会非常密集,因为变化太小了。这些密集点是必须花费的)所以这时候我们按照距离间隔3上报,如果用户到达某个地方后位置没有变化,我们就不会继续上报了。我们只关心位置变化。如果用户的位置没有变化或者变化很小(比如进入公司或者红灯等了很久)其实不需要上报其位置。这个时候我们就不上报了(达到省电的目的)4.切换到后台也能定位和上报后台上报是很有必要的。用户不可能一直运行我们的APP(iOS4已经支持)5.APP由于各种原因终止后(用户主动关闭,系统kill掉),也可以定位上报用户主动关闭APP。几率不高,但是因为系统调度被kill的情况很常见。这时候我们应该也能报到(iOS7已经支持被杀后唤醒)。分析完需求后,我们将介绍如何实施准备工作。首先做一些准备工作在target的Capabilities选项中打开BackgroundModes并勾选Locationupdates,然后在plist中添加NSLocationAlawaysUsageDescription键,在value中键入任意内容即可完成这两步。我们的前期工作已经完成。BackgroundModes是iOS7引入的新功能。NSLocationAlawaysUsageDescription为了增强权限机制,引入的提示说明不加。如果不加这个,就不会使用定位功能。代码定位必须和CLLocationManager打交道。所以我们先定义一个CLLocationManager的子类,根据需求添加。几点定义三个变量@interfaceMMLocationManager:CLLocationManager+(instancetype)sharedManager;@property(nonatomic,assign)CGFloatminSpeed;//最小速度@property(nonatomic,assign)CGFloatminFilter;//最小范围@property(nonatomic,assign)CGFloatminInteval;//UpdateInterval@end在这里解释一下这几个参数minSpeed如果当前移动速度大于这个值,则满足要求(1)时间作为更新依据(minFilter)如果当前移动速度小于这个value,满足要求(2)with范围是更新依据(minInteval)minFilter最小触发范围,用于要求(1)minInteval更新间隔,用于要求(2)接下来是初始化函数-(instancetype)init{self=[superinit];if(self){self.minSpeed=3;self.minFilter=50;self.minInteval=10;self.delegate=self;self.distanceFilter=self.minFilter;self.desiredAccuracy=kCLLocationAccuracyBest;}returnsself;}这里默认值可以be根据需要调整那么位置更新后的处理逻辑其实很简单——(void)locationManager:(CLLocationManager*)managerdidUpdateLocations:(NSArray*)locations{CLLocation*location=locations[0];NSLog(@"%@",location);//根据实际情况调整触发范围[selfadjustDistanceFilter:location];//上传数据[selfuploadLocation:location];}而这个adjustDistanceFilter函数是整个代码的核心,它会动态调整distanceFilter参数根据当前速度满足我我们的需求/***规则:如果速度小于minSpeedm/s,设置触发范围为50m*否则,设置触发范围为minSpeed*minInteval*,此时如果速度变化超过10%,更新当前触发范围(这里的限制是因为distanceFilter不能连续设置,*否则会连续触发uploadLocation)*/-(void)adjustDistanceFilter:(CLLocation*)location{//NSLog(@"adjust:%f",location.speed);if(location.speed0.1f){self.distanceFilter=self.minFilter;}}else{CGFloatlastSpeed=self.distanceFilter/self.minInteval;if((fabs(lastSpeed-location.speed)/lastSpeed>0.1f)||(lastSpeed<0)){CGFloatnewSpeed=(int)(location.speed+0.5f);CGFloatnewFilter=newSpeed*self.minInteval;self.distanceFilter=newFilter;}}}这里需要注意的是参数distanceFilter不能一直设置,因为didUpdateLocations回调会在下一秒后立即触发每次设置后(系统的标准最小更新间隔为1秒。更新频率为1hz)所以distanceFilter只有在变化超过10%时才会被重置。接下来,为了在被杀死的情况下能够正确唤醒,我们还要做最后一步,将其添加到AppDelegate的didFinishLaunchingWithOptions下面的代码中响应ndsToSelector:@selector(requestAlwaysAuthorization)]){[[MMLocationManagersharedManager]requestAlwaysAuthorization];}//这是iOS9引入的新属性,用于后台定位。如果不设置,顶部会出现蓝色条(类似热点连接);}这是因为当被杀死的APP在后台被系统唤醒时,launchOptions中会包含UIApplicationLaunchOptions字段**这时候我们就可以重启定位功能,满足我们需求的定位功能就完成了。为此,我写了一个demo来验证(使用模拟器,选择Debug->Location->FreewayDrive)。结果如下。接下来我们我会讨论几个相关的问题,讨论为什么不使用定时器来控制定位间隔。网上有很多教程都是用NSTimer来实现的,但其实这样不太好。虽然定位间隔是固定的,但是无法解决功耗问题。后台会继续。不管当前位置是否在更新,当然如果你的使用场景是每隔一段时间上传一次,可以使用定时器来处理。如果使用distanceFilter来处理,就会出现一些问题。由于distanceFilter=currentSpeed*minInteval,所以间隔时间是因为速度的变化会有波动,但是这个波动在可以接受的范围内。如果速度加快或减慢,下一次更新时间会相应缩短或延长,但因为我们是在现实生活环境中,速度变化是不可能的。这么快,所以这个错误是可以接受的。此外,我们针对速度修正了distanceFilter,因此整体距离仍将保持在我们的范围内。为什么不使用allowDeferredLocationUpdatesUntilTraveled:timeout:allowDeferredLocationUpdatesUntilTraveled是iOS6新推出的API。从名字我们可以知道这个函数的作用是延迟位置更新,直到移动xx米或者时间超过xx秒。这个功能不正好满足我们所有的要求吗?但万万没想到,事实并非如此。这个功能并不容易使用。接下来是抱怨的时间?(????)为什么说这个功能不好用呢?首先,这个功能有很多要求。我们来看看这个函数。必须满足什么条件才能工作?iPhone5及以后的硬件设备必须支持desiredAccuracy。必须设置为kCLLocationAccuracyBest或kCLLocationAccuracyBestForNavigationdistanceFilter必须设置为kCLDistanceFilterNone。只有APP在后台运行时才会生效。App在前台运行时,不会有延迟处理。只有当系统处于低功耗状态(LowPowerState)时才可能生效。关于iOS中LowPowerState的描述,我在苹果官网的文档中只找到了部分定义。iOS非常擅长让设备在不使用时进入低功耗状态。闲置时,消耗的功率非常少,能量影响也很低。当任务正在积极发生时,系统资源正在被使用,而这些资源需要能量。然而,零星的任务可能会导致设备进入中间状态——既不是空闲的——也不是在设备不做任何事情时处于活动状态。在这些中间状态期间可能没有足够的时间设备在下一个任务执行之前达到绝对空闲。发生这种情况时,能源会被浪费,用户的电池会消耗得更快。根据我的简单理解,这个“**LowPowerState”只在黑屏时有效(不只是锁屏)是可以触发的,只要在电源屏上有任何操作(即使是push)都会使APP退出这个状态,同时如果处于充电状态,则无法进入,我尝试在真机和模拟器上使用这个API,结果APP还是以频率结束1HZ的是定位(设置kCLDistanceFilterNone的原因)虽然在指定时间后成功回调了locationManager:didFinishDeferredUpdatesWithError:,但是结果还是没有deffer。于是查了一下,原来这个函数不能直接调试,原因是:itdoesnotsupportthesimulatordeferredLocationUpdatesAvailable用于检测设备是否支持模拟器,会返回NO。不支持真机调试,因为Xcode会阻止程序休眠during调试,使程序无法进入低功耗状态。结论就是……这东西连debug都调试不了,我也没那么多时间。到外面去测试一下这个东西。。。另外,用我上面说的方法基本可以满足需求。。。所以我已经放弃继续研究这个API了,因为即使我用了这个东西,也只是锦上添花.如果哪位同学知道如何正确使用这个请留言告诉我一下,万分感谢!摘要中的演示可以在这里找到。另外,demo中使用了Realm来存储数据(模拟上传操作)。有兴趣的同学可以看看