iOS 后台定位
http://www.cocoachina.com/ios/20150724/12735.html
前言
之前的文章说过 我现在做的是LBS定位的社交APP 其中主要的一个功能就是能够实时定位社交圈中各个成员的位置 后台实时上传位置则是非常重要的一个技术点 接下来就来说说我关于这方面的实践经验
需求
先来看看实现这个功能的具体需求是什么 由于我们是实时定位的生活类社交APP 所以我们需要做到一下几点
1. 如果用户的位置在持续变化 则隔一段时间上报一次
由于我们希望能够实时的将用户的位置变化反馈在APP里 所以定时的上报是刚需
2. 如果用户的移动速度很慢 则隔一段距离上报一次
如果用户是低速率的状态(比如步行的移动速度大概就是1m/s左右) 这个时候如果还按(1)中的方式来上报的话 由于变化太小 地图上的点会非常的密集 这种数据的意义不大(而且如果要做轨迹服务的话 这些密集点都是必须有花掉的) 所以这时候我们按照距离间隔来上报
3. 如果用户的位置在到达某处后没有变化 则不继续上报
我们只关心位置的变化 如果用户的位置没有变化或者变化很小 其实是不需要上报其位置的(比如进入的公司 或者等一个很长时间的红灯) 这时候我们就不上报(以达到省电的目的)
4. 切换到后台也要能定位上报
后台上报是必须的 用户不可能一直运行着我们的APP (iOS4开始就支持了)
5. APP因各种原因终止运行后(用户主动关闭, 系统杀掉) 也要能定位上报
用户主动关闭APP的几率不大 但是因系统调度被杀掉的情况是很普遍的 这个时候我们也要能够上报 (iOS7开始已支持被杀掉后唤醒)
分析完需求 接下来就开始介绍如何实现
准备
首先做一些准备工作
在target的Capabilities选项中打开Background Modes 并勾选Location updates

然后在plist中添加NSLocationAlawaysUsageDescription的键 在value中随便键入任何内容

完成这两步 我们的前期工作就完成了 Background Modes是iOS7带入的新功能 而NSLocationAlawaysUsageDescription为了增强权限机制引入的提示描述 不添加这个的话 定位功能可是使用不了的
代码
定位肯定要跟CLLocationManager打交道 所以我们先定义一个CLLocationManager的子类 并根据需求中的几点定义三个变量
|
1
2
3
4
5
6
|
@interface MMLocationManager : CLLocationManager+ (instancetype)sharedManager;@property (nonatomic, assign) CGFloat minSpeed; //最小速度@property (nonatomic, assign) CGFloat minFilter; //最小范围@property (nonatomic, assign) CGFloat minInteval; //更新间隔@end |
这里解释一下这几个参数
minSpeed 如果当前运动速度大于此值 则满足需求(1) 以时间为更新依据(minFilter) 如果当前运动速度小于此值 则满足需求(2) 以范围为更新依据(minInteval)
minFilter 最小的触发范围 用于需求(1)
minInteval 更新间隔 用于需求(2)
接下来是初始化函数
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
- (instancetype)init{ self = [super init]; if ( self ) { self.minSpeed = 3; self.minFilter = 50; self.minInteval = 10; self.delegate = self; self.distanceFilter = self.minFilter; self.desiredAccuracy = kCLLocationAccuracyBest; } return self;} |
这里的默认值可以根据需求来调整
然后是位置更新后的处理逻辑 其实也非常的简单
|
1
2
3
4
5
6
7
8
9
|
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{ CLLocation *location = locations[0]; NSLog(@"%@",location); //根据实际情况来调整触发范围 [self adjustDistanceFilter:location]; //上传数据 [self uploadLocation:location];} |
而这个adjustDistanceFilter函数 就是整个代码的核心 会根据当前速度来动态的调整distanceFilter这个参数 以满足我们的需求
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
/*** 规则: 如果速度小于minSpeed m/s 则把触发范围设定为50m* 否则将触发范围设定为minSpeed*minInteval* 此时若速度变化超过10% 则更新当前的触发范围(这里限制是因为不能不停的设置distanceFilter,* 否则uploadLocation会不停被触发)*/- (void)adjustDistanceFilter:(CLLocation*)location{// NSLog(@"adjust:%f",location.speed); if ( location.speed < self.minSpeed ) { if ( fabs(self.distanceFilter-self.minFilter) > 0.1f ) { self.distanceFilter = self.minFilter; } } else { CGFloat lastSpeed = self.distanceFilter/self.minInteval; if ( (fabs(lastSpeed-location.speed)/lastSpeed > 0.1f) || (lastSpeed < 0) ) { CGFloat newSpeed = (int)(location.speed+0.5f); CGFloat newFilter = newSpeed*self.minInteval; self.distanceFilter = newFilter; } }} |
这里要注意到的是distanceFilter这个参数不能一直进行设置 因为每次设置完以后 再接下来的一秒以后 会立即触发didUpdateLocations回调(系统的标准最短更新间隔是1秒 即更新频率为1hz) 所以这里只有当变化超过10%的时候才会重置distanceFilter
接下来 为了能够正确的在被杀掉的情况下被唤醒 我们还要做最后一步操作 在AppDelegate的didFinishLaunchingWithOptions中加入下面的代码
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
if ([launchOptions objectForKey:UIApplicationLaunchOptionsLocationKey]) { if ( [[MMLocationManager sharedManager] respondsToSelector:@selector(requestAlwaysAuthorization)] ) { [[MMLocationManager sharedManager] requestAlwaysAuthorization]; } //这是iOS9中针对后台定位推出的新属性 不设置的话 可是会出现顶部蓝条的哦(类似热点连接) if ( [self respondsToSelector:@selector(allowsBackgroundLocationUpdates)]) { [MMLocationManager sharedManager].allowsBackgroundLocationUpdates = YES; } [[MMLocationManager sharedManager] startUpdatingLocation];} |
这是因为被杀掉的APP 在后台被系统唤醒时 launchOptions会包含UIApplicationLaunchOptionsLocationKey**字段来进行标识 这时我们再重新启动定位功能即可
至此 满足我们需求的定位功能就完成了 为此我写了一个demo来验证(使用模拟器 然后选择Debug->Location->Freeway Drive) 结果如下


接下来我们会讨论一下相关的几个问题
讨论
为什么不用定时器来控制定位间隔
网上有很多教程是用NSTimer来实现的 但是其实这样不是很好 虽然定位的间隔是固定的 但是耗电的问题无法解决 后台会持续的更新定位 无论当前的位置是否在更新 当然 如果你的使用场景就是要每隔一段时间来上传 就可以使用定时器来处理
使用distanceFilter来处理 会有些什么问题
由于distanceFilter=currentSpeed*minInteval 那么间隔的时间因为速度的变化而会有波动 但是这个波动是在可接受范围的 如果速度加快或者变慢 那么下一次的更新时间则会相应的缩短或者变长 但是因为我们是在真实生活环境中 速度的变化不可能那么快 所以这个误差是可以接受的 另外我们对distanceFilter针对速度进行矫正 因而总体来说 间隔还是会保持在我们与其的范围内的
为什么不使用allowDeferredLocationUpdatesUntilTraveled:timeout:
allowDeferredLocationUpdatesUntilTraveled是iOS6推出的一个新的API 看名字我们可以知道这个函数的作用是延迟位置更新 直到移动了xx米或者时间超过了xx秒 那么这个函数不正好满足了我们的所有要求么? 可是万万没想到 事情并不是这样的 这个函数并不好用
接下来是吐槽时间 ?(????)
为什么说这个函数不好用呢? 首先 这个函数的要求很多 我们来看看要这个函数起作用要满足哪些条件
必须iPhone5以及之后的硬件设备才支持
desiredAccuracy必须设置为kCLLocationAccuracyBest或者kCLLocationAccuracyBestForNavigation
distanceFilter必须设置为kCLDistanceFilterNone
只在APP运行在后台时生效 前台运行时是不会进行延迟处理的
只有系统在低功耗(Low Power State)的时候才有可能生效
关于Low Power State在iOS中的描述 我只在苹果官网的文档中找到部分定义
iOS is very good at getting a device into a low power state when it’s not being used. At idle, very little power is drawn and energy impact is low. When tasks are actively occurring, system resources are being used and those resources require energy. However, sporadic tasks can cause the device to enter an intermediate state—neither idle nor active—when the device isn’t doing anything. There may not be enough time during these intermediate states for the device to reach absolute idle before the next task executes. When this occurs, energy is wasted and the user’s battery drains faster.
据我简单的了解 这个**Low Power State”只有在黑屏的状态下(不只是锁屏)才有可能触发 只要有任何电量屏幕的操作(就连推送也算) 都会使APP退出这个状态 同时 如果在充电状态下 也是无法进入的
我尝试在真机和模拟器上使用这个API 但结果APP还是以1HZ的频率在定位(设置了kCLDistanceFilterNone的原因)
虽然locationManager:didFinishDeferredUpdatesWithError:在指定的时间后成功的回调了 但是结果还是没有deffer 于是我查了一下 原来这个函数无法直接进行调试的 因为:
不支持模拟器 deferredLocationUpdatesAvailable用于检测设备是否支持 模拟器会返回NO
不支持真机调试 因为调试时Xcode会阻止程序休眠 导致程序无法进入低功耗状态
结论就是…这个东西连调试都没办法 所以我也没有那么多时间跑到外面去测试这个东西… 况且使用我上述的方法已经基本可以满足需求… 所以我已放弃继续研究这个API了 因为就算使用了这个东西 也仅仅是锦上添花而已
如果有哪些同学知道如何正确的使用这个东西 请留言告诉我 万分感谢!
小结
文中的demo可以在这里找到 另外demo中用到了Realm来存储数据(模拟上传操作) 有兴趣的同学可以看一下
iOS 后台定位的更多相关文章
- iOS后台定位实现
iOS后台定位实现 (2013-01-24 16:43:12) 工作中碰到一个定位的应用场景:app需要在后台运行,实时上传用户地理位置. 苹果对iOS的规范性在提升了app的品质的同时也 ...
- iOS 后台定位被拒注意事项
iOS 后台定位被拒的原因很简单就是没有达到苹果对后台定位的要求. 本地要求: 1.在plist文件中添加字段 "Privacy - Location Always Usage Descri ...
- iOS后台定位,实时向服务器发送最新位置
第一步,开启后台模式,选中定位,选择project --> capabilities-->Backgorund Modes --> Location updates 如图: Past ...
- iOS后台定位时授权提示一闪而过的解决办法
今天做后台定位时,授权提示"允许 XXX 在您并未使用该应用时范文您的位置吗?"总是一闪而过,点不到,或者压根就不弹出.后来找到了解决问题的方法,那就是:将CLLocationMa ...
- iOS 后台定位审核被拒How to clarify the purpose of its use in the locatio
4.5 - Apps using background location services must provide a reason that clarifies the purpose of th ...
- iOS:后台定位并实时向服务器发送位置
第一步:开启后台模式,选中定位,选择project --> capabilities-->Backgorund Modes --> Location updates 如图: 第二步: ...
- iOS开发雕虫小技之傻瓜式定位神器-超简单方式解决iOS后台定时定位
1.概述 由于公司一款产品的需求,最近一直在研究iOS设备的后台定位.主要的难点就是,当系统进入后台之后,程序会被挂起,届时定时器.以及代码都不会Run~ 所以一旦用户将我的App先换到了后台,我的定 ...
- iOS 后台持续定位详解(支持ISO9.0以上)
iOS 后台持续定位详解(支持ISO9.0以上) #import <CoreLocation/CoreLocation.h>并实现CLLocationManagerDelegate 代理, ...
- iOS 9适配系列教程:后台定位
http://www.cocoachina.com/ios/20150624/12200.html Demo:GitHub地址 [iOS9在定位的问题上,有一个坏消息一个好消息]坏消息:如果不适配iO ...
随机推荐
- chgrp权限命令
功能说明:变更文件或目录的所属群组. 语 法:chgrp [-cfhRv][--help][--version][所属群组][文件或目录...] 或 chgrp [-cfhRv][--help][-- ...
- c++使用优先队列时自定义优先出队顺序(和sort)
优先队列也是一种先进先出的数据结构,元素从队尾入队,从队头出队,但是优先队列相较一般队列多了一个判断优先级的功能,在当前队列中,优先级最高的元素将被第一个删除. 先看一下优先队列的定义 templat ...
- UVAL3700
Interesting Yang Hui Triangle 题目大意:杨辉三角第n + 1行不能整除p(p是质数)的数的个数 题解: lucas定理C(n,m) = πC(ni,mi) (mod p) ...
- leetcode 57 Insert Interval & leetcode 1046 Last Stone Weight & leetcode 1047 Remove All Adjacent Duplicates in String & leetcode 56 Merge Interval
lc57 Insert Interval 仔细分析题目,发现我们只需要处理那些与插入interval重叠的interval即可,换句话说,那些end早于插入start以及start晚于插入end的in ...
- #queue队列 #生产者消费者模型
#queue队列 #生产者消费者模型 #queue队列 #有顺序的容器 #程序解耦 #提高运行效率 #class queue.Queue(maxsize=0) #先入先出 #class queue.L ...
- LTIME16小结(CodeChef)
题目链接 最后一题是Splay...还没有学会..蒟蒻!!! A /****************************************************************** ...
- C++ string(STL)
发现字符串问题中 string 好厉害- string类的构造函数: string(const char *s); //用c字符串s初始化 string(int n,char c); //用n个字符c ...
- 我悲惨的人生,该死的UPX壳,谁能救救我
一个程序,被加了UPX壳... 结果加壳的人把UPX脱壳的关键参数都给删除掉了,我现在连脱壳都脱不掉... 我从网上下载了UPX最新3.91版本的壳,复制了两个UPX.exe,本来互相加壳和脱壳都没 ...
- 如何把pdf文档转化为word
在工作中常常遇到大量的pdf文档,再加工进行处理文件,特别的不方便,需要转换为WORD. 尝试如下: 使用wps自带的工具转换,提示需要是会员才能进行.否则只能进行5页以下的转换. 再想是不是又有个这 ...
- C# Socket流数据大小端读写封装
网络数据是大端模式,而c#中的数据小端结构,那么在读写网络数据的时候需要进行转换.c#类库IPAddress已经封装了大小端的转换. 封装代码如下: using System.IO; using ...