前言
我現(xiàn)在做的是LBS定位的社交APP 其中主要的一個功能就是能夠?qū)崟r定位社交圈中各個成員的位置后臺實時上傳位置則是非常重要的一個技術(shù)點 接下來就來說說我關(guān)于這方面的實踐經(jīng)驗
需求
先來看看實現(xiàn)這個功能的具體需求是什么 由于我們是實時定位的生活類社交APP 所以我們需要做到一下幾點
1. 如果用戶的位置在持續(xù)變化 則隔一段時間上報一次
由于我們希望能夠?qū)崟r的將用戶的位置變化反饋在APP里 所以定時的上報是剛需
2. 如果用戶的移動速度很慢 則隔一段距離上報一次
如果用戶是低速率的狀態(tài)(比如步行的移動速度大概就是1m/s左右) 這個時候如果還按(1)中的方式來上報的話 由于變化太小 地圖上的點會非常的密集 這種數(shù)據(jù)的意義不大(而且如果要做軌跡服務(wù)的話 這些密集點都是必須優(yōu)化掉的) 所以這時候我們按照距離間隔來上報
3. 如果用戶的位置在到達某處后沒有變化 則不繼續(xù)上報
我們只關(guān)心位置的變化 如果用戶的位置沒有變化或者變化很小 其實是不需要上報其位置的(比如進入的公司 或者等一個很長時間的紅燈) 這時候我們就不上報(以達到省電的目的)
4. 切換到后臺也要能定位上報
后臺上報是必須的 用戶不可能一直運行著我們的APP (iOS4開始就支持了)
5. APP因各種原因終止運行后(用戶主動關(guān)閉, 系統(tǒng)殺掉) 也要能定位上報
用戶主動關(guān)閉APP的幾率不大 但是因系統(tǒng)調(diào)度被殺掉的情況是很普遍的 這個時候我們也要能夠上報 (iOS7開始已支持被殺掉后喚醒)
分析完需求 接下來就開始介紹如何實現(xiàn)
準備
首先做一些準備工作
在target的Capabilities選項中打開Background Modes并勾選Location updates
然后在plist中添加NSLocationAlawaysUsageDescription的鍵 在value中隨便鍵入任何內(nèi)容
完成這兩步 我們的前期工作就完成了Background Modes是iOS7帶入的新功能 而NSLocationAlawaysUsageDescription為了增強權(quán)限機制引入的提示描述 不添加這個的話 定位功能可是使用不了的 之后我們要調(diào)用requestAlwaysAuthorization提示中就會顯示這個描述
在評論中有朋友問起 為什么要使用requestAlwaysAuthorization而不使用requestWhenInUseAuthorization呢 我借用一張圖來說明一下
簡單來說 兩者的區(qū)別在于前者支持Region Monitoring和Significant Location Changes而后者不支持
但其實還有一個非常重要的區(qū)別 就是前者支持程序被殺掉后的系統(tǒng)自動喚醒而后者不支持 這是非常重要的一個區(qū)別
定位肯定要跟CLLocationManager打交道 所以我們先定義一個CLLocationManager的子類 并根據(jù)需求中的幾點定義三個變量
這里解釋一下這幾個參數(shù)
minSpeed如果當前運動速度大于此值 則滿足需求(1) 以時間為更新依據(jù)(minFilter) 如果當前運動速度小于此值 則滿足需求(2) 以范圍為更新依據(jù)(minInteval)
minFilter最小的觸發(fā)范圍 用于需求(1)
minInteval更新間隔 用于需求(2)
接下來是初始化函數(shù)
這里的默認值可以根據(jù)需求來調(diào)整
然后是位置更新后的處理邏輯 其實也非常的簡單
而這個adjustDistanceFilter函數(shù) 就是整個代碼的核心 會根據(jù)當前速度來動態(tài)的調(diào)整distanceFilter這個參數(shù) 以滿足我們的需求
這里要注意到的是distanceFilter這個參數(shù)不能一直進行設(shè)置 因為每次設(shè)置完以后 再接下來的一秒以后 會立即觸發(fā)didUpdateLocations回調(diào)(系統(tǒng)的標準最短更新間隔是1秒 即更新頻率為1hz) 所以這里只有當變化超過10%的時候才會重置distanceFilter
接下來 為了能夠正確的在被殺掉的情況下被喚醒 我們還要做最后一步操作 在AppDelegate的didFinishLaunchingWithOptions中加入下面的代碼
接下來我們會討論一下相關(guān)的幾個問題
討論
為什么不用定時器來控制定位間隔
網(wǎng)上有很多教程是用NSTimer來實現(xiàn)的 但是其實這樣不是很好 雖然定位的間隔是固定的 但是耗電的問題無法解決 后臺會持續(xù)的更新定位 無論當前的位置是否在更新 當然 如果你的使用場景就是要每隔一段時間來上傳 就可以使用定時器來處理
使用distanceFilter來處理 會有些什么問題
由于distanceFilter=currentSpeed*minInteval 那么間隔的時間因為速度的變化而會有波動 但是這個波動是在可接受范圍的 如果速度加快或者變慢 那么下一次的更新時間則會相應(yīng)的縮短或者變長 但是因為我們是在真實生活環(huán)境中 速度的變化不可能那么快 所以這個誤差是可以接受的 另外我們對distanceFilter針對速度進行矯正 因而總體來說 間隔還是會保持在我們與其的范圍內(nèi)的
為什么不使用allowDeferredLocationUpdatesUntilTraveled:timeout:
allowDeferredLocationUpdatesUntilTraveled是iOS6推出的一個新的API 看名字我們可以知道這個函數(shù)的作用是延遲位置更新 直到移動了xx米或者時間超過了xx秒 那么這個函數(shù)不正好滿足了我們的所有要求么? 可是萬萬沒想到 事情并不是這樣的 這個函數(shù)并不好用
接下來是吐槽時間 ?(????)
為什么說這個函數(shù)不好用呢? 首先 這個函數(shù)的要求很多 我們來看看要這個函數(shù)起作用要滿足哪些條件