iOS定位服務(wù)設(shè)計實例一則

iOS定位服務(wù)設(shè)計實例一則

當(dāng)前侄柔,越來越多的移動應(yīng)用基于LBS(位置服務(wù))構(gòu)建業(yè)務(wù),LBS可以說是移動應(yīng)用浪潮的基石多搀。每次說到LBS帅掘,我們的第一反應(yīng)就是以百度地圖SDK為代表的第三方框架(類似的還有高德,騰訊出品的地圖SDK)绷蹲,刻意忽略原生框架Core Location和Map Kit棒卷。但問題是:前者真的要比后者好嗎?我們對二者到底了解多少祝钢?


A. 定位服務(wù)和地圖服務(wù)

位置服務(wù)由兩部分組成:

  1. 定位:即設(shè)備位置信息比规。
  2. 地圖:即顯示地圖和標(biāo)注地圖。

本文重點介紹定位(服務(wù))拦英。


A.1 定位服務(wù)提供的信息

通常蜒什,定位服務(wù)需要提供的信息可劃分為兩類:

  1. 設(shè)備當(dāng)前坐標(biāo),海拔龄章,朝向等基本位置信息吃谣。

    這些信息由設(shè)備上的相關(guān)硬件直接提供,不依賴服務(wù)器做裙。

  2. 需要檢索服務(wù)器的地理信息岗憋,如坐標(biāo)反地理編碼(城市,地區(qū)锚贱,街道仔戈,門牌號,名稱等等)拧廊,以及基于關(guān)鍵字的poi查詢(興趣點)等监徘。

    這些信息通過向服務(wù)器檢索獲取,可以視為是基于第一類信息的延伸吧碾。

B. 技術(shù)方案

經(jīng)考量凰盔,使用如下策略獲取這兩類信息:

  • 基本位置信息:通過原生框架Core Location獲取。原因如下:

    • 配置項多倦春,有助于精細化控制服務(wù)户敬;
    • 信息全面,來源統(tǒng)一睁本;
    • 提供多個節(jié)能選項尿庐;
    • 相較于第三方框架(如百度地圖SDK),無須認證(否則如果百度服務(wù)掛了呢堰,搞的最基本的定位服務(wù)也不能用)抄瑟;
  • 地理信息檢索:通過百度地圖SDK獲取。原因如下:

    • 檢索種類多枉疼,信息全面(特別是poi信息)皮假;
    • 模塊清晰,使用簡單往衷;
    • 可以配合百度地圖服務(wù)一起使用钞翔;

下面,我們開始編寫自己的定位服務(wù)??

C. 定位服務(wù)

假設(shè)我們要編寫一個名為LocationService的定位服務(wù)席舍,負責(zé)提供定位信息布轿。根據(jù)需求,我們?yōu)槠涠x如下Interface:

/// 單例来颤,全局唯一入口
+ (instancetype)defaultService;

@property (nonatomic, strong, readonly) DDPCLocation *ddpcLocation;
@property (nonatomic, assign, readonly) CLLocationCoordinate2D coordinate;

具體功能&特性如下:


C.1 申請位置信息訪問權(quán)限

眾所周知汰扭,位置信息訪問權(quán)限有兩種:

  • When In Use(app在前臺時)
  • Always(app運行時,不管在前臺還是后臺)

大家也許會注意到福铅,有些app被切換至后臺萝毛,狀態(tài)欄處會出現(xiàn)藍條,顯示一條信息:xxxx正在使用你的位置滑黔。
所以笆包,上述權(quán)限除了字面所示的區(qū)別之外环揽,還有一點需要注意:app被切換至后臺,如果開啟了后臺位置更新庵佣,則:

  • When In Use:顯示藍條
  • Always:不顯示藍條

合理的解釋是歉胶,對于Always來說,系統(tǒng)認為用戶已經(jīng)充分知曉app會在后臺繼續(xù)訪問位置信息巴粪,所以不必提示通今;而對于When In Use來說,系統(tǒng)認為有必要提醒用戶app正在后臺繼續(xù)訪問位置信息肛根,超出了授權(quán)的范圍辫塌。

請求授權(quán)的代碼如下:

/*** LocationService init ***/
// 請求授權(quán),always
[self.locationManager requestAlwaysAuthorization];

C.2 使用CLLocationManager獲取基本位置信息

使用CLLocationManager進行定位的優(yōu)勢如下:

  1. 系統(tǒng)級別的定位信息緩存派哲,臼氨,即使CLLocationManager對象剛創(chuàng)建,也可以從中讀取到最近一次定位信息狮辽。這是因為iOS在系統(tǒng)層面管理定位行為一也。
  2. 無須任何認證,即可使用喉脖,從而保證了服務(wù)的穩(wěn)定性椰苟。
  3. 有多個節(jié)能設(shè)置。由于定位非常耗電树叽,為了增加設(shè)備續(xù)航舆蝴,節(jié)能就變得異常重要。例如:

/// 使用定位信息的活動類型
@property(assign, nonatomic) CLActivityType activityType;
/// 當(dāng)設(shè)備位置可能不再變化時题诵,系統(tǒng)是否可以自動暫停位置更新
@property(assign, nonatomic) BOOL pausesLocationUpdatesAutomatically;
/// 移動多少距離洁仗,才觸發(fā)位置更新
@property(assign, nonatomic) CLLocationDistance distanceFilter;


C.3 當(dāng)前坐標(biāo)一直有效

當(dāng)前,用戶未授權(quán)訪問位置信息時除外性锭。

正如C.1.2所述赠潦,iOS在系統(tǒng)層面緩存了最新定位信息。事實上草冈,CLLocationManager對象一旦創(chuàng)建她奥,就可以從其屬性location處獲取最近一次定位信息,代碼如下:

/*** LocationService init ***/
// 讀取locationManager中的位置緩存
self.rawLocation = self.locationManager.location;

隨后怎棱,一旦有位置更新哩俭,我們再將其緩存起來使用:

/*** CLLocationManagerDelegate ***/ 
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations {
    // 每次位置更新,記錄新的坐標(biāo)
    self.rawLocation = locations.lastObject;
}

提醒一點拳恋,上述方法中凡资,最新位置的時間戳總是和manager.location的時間相同。也就是說谬运,CLLocationManager首先保存最新位置隙赁,再調(diào)用進行回調(diào)垦藏。


C.4 減少不必要的位置更新,盡可能節(jié)能

正常情況下伞访,一旦開始監(jiān)聽膝藕,就會源源不斷的收到位置更新,即使設(shè)備在原地保持不動咐扭。還要注意,更新的頻率很高滑废,粗略估算蝗肪,平均每10秒左右就會有一次更新。很明顯蠕趁,這種信息重復(fù)的高頻率更新并不是必須的薛闪,很多時候"有移動,才更新"的模式更適合業(yè)務(wù)需求俺陋。

例如豁延,后臺要求設(shè)備每移動10m,就上報一次位置腊状,那么可以按照如下配置CLLocationManager:

/*** _locationManager = [[CLLocationManager alloc] init]; ***/
_locationManager.distanceFilter = 10.0;
_locationManager.desiredAccuracy = kCLLocationAccuracyBest;

這樣诱咏,既保證了定位的精度,也減少了更新頻率缴挖,節(jié)省設(shè)備電力袋狞。


C.5 處理位置授權(quán)狀態(tài)變更

下述回調(diào)不僅在授權(quán)狀態(tài)變更時觸發(fā),也會在CLLocationMananger創(chuàng)建后觸發(fā):

- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status

所以映屋,這個方法可以是許多關(guān)鍵邏輯的入口苟鸯。例如,下面的代碼首先判斷授權(quán)狀態(tài):如果得到授權(quán)棚点,則開始監(jiān)聽位置更新早处;讀取定位緩存;進行反地理編碼查詢瘫析。如果未得到授權(quán)砌梆,則可以嘗試提醒用戶。

/*** locationManager:didChangeAuthorizationStatus: ***/ 

    if (self.isAuthorized) { // 已授權(quán)
        // 刷新位置
        [self.locationManager startUpdatingLocation];
        
        // 讀取locationManager中的位置緩存
        self.rawLocation = self.locationManager.location;
        // 反地理編碼
        [self retrieveCurrentLocation];
        // 反地理編碼timer
        [self setupTimer];
    } else {
        // TODO: 是否要提醒用戶打開位置服務(wù)
    }

C.6 系統(tǒng)坐標(biāo)轉(zhuǎn)換為百度坐標(biāo)

一般來說颁股,整個業(yè)務(wù)體系會使用同一套坐標(biāo)系么库,這里假設(shè)是百度坐標(biāo)。原生定位框架給出的坐標(biāo)是地球坐標(biāo)甘有,需要客戶端進行轉(zhuǎn)換诉儒。

```s
不同坐標(biāo)系:地球坐標(biāo),火星坐標(biāo)和百度坐標(biāo)
- 地球坐標(biāo)(WGS84):國際標(biāo)準(zhǔn)亏掀,通過Core Location獲取的坐標(biāo)使用這個坐標(biāo)系忱反;
- 火星坐標(biāo)(GCJ-02):中國標(biāo)準(zhǔn)泛释,高德地圖使用這個坐標(biāo)系;
- 百度坐標(biāo)(BD-09):百度地圖使用的坐標(biāo)系温算;

```

百度地圖SDK在計算工具模塊給出了現(xiàn)成的轉(zhuǎn)換方式:

// 原始坐標(biāo)
CLLocationCoordinate2D coor = CLLocationCoordinate2DMake(39.90868, 116.3956);
 
// 轉(zhuǎn)換WGS84坐標(biāo)至百度坐標(biāo)(加密后的坐標(biāo))
NSDictionary *testdic = BMKConvertBaiduCoorFrom(coor,BMK_COORDTYPE_GPS);
 
// 解密加密后的坐標(biāo)字典
// 轉(zhuǎn)換后的百度坐標(biāo)
CLLocationCoordinate2D baiduCoor = BMKCoorDictionaryDecode(testdic);

C.7 提供并更新反地理編碼信息

反地理編碼的具體實現(xiàn)依賴百度地圖SDK怜校。

至于更新策略,由于每次查詢都是一次網(wǎng)絡(luò)請求注竿,所以有兩種:

  1. 使用時再檢索茄茁,異步實現(xiàn)。適合用量較少的場景巩割;
  2. 定期檢索裙顽,保存結(jié)果,同步實現(xiàn)宣谈。適合用量較大的場景愈犹;

根據(jù)實際情況,我們選用第二種闻丑。

  1. cllocationmanager的distanfiler的問題:
distanceFileter能夠只在移動特定距離時漩怎,才調(diào)用更新方法,配合locationManager的location屬性嗦嗡,后者是最新的勋锤,但只是距離未達標(biāo),才沒有調(diào)用更新方法侥祭,

D. 地理信息檢索

負責(zé)檢索工具類名為GeoSearchOperation怪得,其是對百度地圖SDK檢索操作的封裝。根據(jù)需求卑硫,我們?yōu)槠涠x如下Interface:

/// 反地理編碼查詢
- (void)reverseGeoCodeSearchWithCoordinate:(CLLocationCoordinate2D)coor completionHandler:(void (^)(BOOL isSuccessful, NSArray *results))handler;

/// 檢索室內(nèi)poi徒恋,如果city傳nil,則表示使用當(dāng)前定位所在城市欢伏,keyword必傳
- (void)poiSearchWithCity:(NSString *)city keyword:(NSString *)keyword completionHandler:(void (^)(BOOL isSuccessful, NSArray *results))handler;

具體功能&特性如下:


D.1 使用百度地圖SDK檢索模塊實現(xiàn)

在信息檢索方面入挣,由于Core Location框架提供的信息有限,本土化的第三方位置服務(wù)框架表現(xiàn)更優(yōu)秀硝拧。


D.2 區(qū)分檢索類型

兩種檢索類型:

  1. 反地理編碼:根據(jù)坐標(biāo)查詢街道径筏,城市等信息;
  2. poi:根據(jù)關(guān)鍵字障陶,查詢"興趣點"(point of interest)滋恬;

D.3 問題

百度地圖SDK的接入,會帶來兩個問題:

  1. 百度地圖SDK要求在app啟動時進行注冊抱究,否則無法調(diào)用服務(wù)恢氯;
  2. 百度地圖服務(wù)必須配合SDK自帶的定位服務(wù)使用;

因此,還需處理以下邏輯:

  1. 注冊百度地圖SDK勋拟;
  2. 封裝SDK定位服務(wù)勋磕,供百度地圖服務(wù)使用;

E. 注冊百度地圖SDK

BMKAuthentication負責(zé)注冊百度地圖SDK敢靡,并處理可能發(fā)生的錯誤挂滓。根據(jù)需求,我們?yōu)槠涠x如下Interface:

/// 單例啸胧,全局唯一入口
+ (instancetype)defaultAuthentication;

/// 注冊百度地圖
- (void)authenticate;

具體功能&特性如下:


E.1 盡早注冊

在app生命周期的初始階段赶站,盡可能早的完成注冊。由于業(yè)務(wù)圍繞LBS展開纺念,所以app在很多方面強依賴于百度SDK亲怠;無法調(diào)用SDK服務(wù),意味著業(yè)務(wù)癱瘓柠辞。

一般來說,在app啟動之初注冊即可主胧,但最好先于其他邏輯:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [[DDBMKAuthentication defaultAuthentication] authenticate];
    // 其他啟動邏輯
}


E.2 失敗重試

只在由于網(wǎng)絡(luò)原因失敗時重試叭首,其他情況一律不重試,因為沒有意義踪栋。例如焙格,因為ak失效或配額超限而注冊失敗,不管怎么重試夷都,都毫無意義眷唉。

BMKMapManager通過下述回調(diào)方法告訴我們網(wǎng)絡(luò)是否存在問題:

- (void)onGetNetworkState:(int)iError {
    if (iError == 0) {
        self.hasNetworkFailure = NO;
    } else {
        NSLog(@"網(wǎng)絡(luò)錯誤,百度地圖注冊失敗");
        self.hasNetworkFailure = YES;
    }
}

F. 封裝SDK定位服務(wù)

DDBMKLocationService是對百度地圖SDK定位服務(wù)的封裝囤官。根據(jù)需求冬阳,我們?yōu)槠涠x如下Interface:

/// 進行定位,定位成功党饮,代理方法被調(diào)用肝陪。注意,只定位一次刑顺。
- (void)locate;

/// delegate
@property (nonatomic, weak) id<DDBMKLocationServiceDelegate> delegate;
/** 百度 location */
@property (nonatomic, strong, readonly) BMKUserLocation *BMKUserLocation;

此外氯窍,其還定義了協(xié)議DDBMKLocationServiceDelegate,作為定位成功后的回調(diào)蹲堂。

@protocol DDBMKLocationServiceDelegate <NSObject>

/// 百度sdk定位更新狼讨,會調(diào)用這個方法
- (void)BMKLocationService:(DDBMKLocationService *)BMKLocationService didUpdateBMKUserLocation:(BMKUserLocation *)userLocation;

@end

具體功能&特性如下:


F.1 僅定位一次,不持續(xù)定位

每次調(diào)用定位方法locate柒竞,僅定位一次政供,一旦回調(diào),不管成功或失敗,都停止鲫骗,不持續(xù)定位犬耻。從而避免了在功能上與LocationService重疊,也節(jié)省了資源执泰。

// 定位
- (void)locate {
    [self.BMKLocationService startUserLocationService];
}

#pragma mark - BMKLocationServiceDelegate

- (void)didUpdateBMKUserLocation:(BMKUserLocation *)userLocation {
    NSLog(@"百度定位成功?");
    
    self.BMKUserLocation = userLocation;
    // 停止定位
    [self.BMKLocationService stopUserLocationService];
}


F.2 隨地圖對象釋放枕磁,不駐留內(nèi)存

由于這個類僅服務(wù)百度地圖,所以其應(yīng)該隨著地圖的創(chuàng)建而創(chuàng)建术吝,釋放而釋放计济。

參考資料:

  1. Location and Maps Programming Guide
  2. 百度地圖-iOSSDK-開發(fā)指南
  3. 百度坐標(biāo)(BD09)、國測局坐標(biāo)(火星坐標(biāo)排苍,GCJ02)沦寂、和WGS84坐標(biāo)系互轉(zhuǎn)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市淘衙,隨后出現(xiàn)的幾起案子传藏,更是在濱河造成了極大的恐慌,老刑警劉巖彤守,帶你破解...
    沈念sama閱讀 222,681評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件毯侦,死亡現(xiàn)場離奇詭異,居然都是意外死亡具垫,警方通過查閱死者的電腦和手機侈离,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來筝蚕,“玉大人卦碾,你說我怎么就攤上這事∑鹂恚” “怎么了洲胖?”我有些...
    開封第一講書人閱讀 169,421評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長坯沪。 經(jīng)常有香客問我宾濒,道長,這世上最難降的妖魔是什么屏箍? 我笑而不...
    開封第一講書人閱讀 60,114評論 1 300
  • 正文 為了忘掉前任绘梦,我火速辦了婚禮,結(jié)果婚禮上赴魁,老公的妹妹穿的比我還像新娘卸奉。我一直安慰自己,他們只是感情好颖御,可當(dāng)我...
    茶點故事閱讀 69,116評論 6 398
  • 文/花漫 我一把揭開白布榄棵。 她就那樣靜靜地躺著凝颇,像睡著了一般。 火紅的嫁衣襯著肌膚如雪疹鳄。 梳的紋絲不亂的頭發(fā)上拧略,一...
    開封第一講書人閱讀 52,713評論 1 312
  • 那天,我揣著相機與錄音瘪弓,去河邊找鬼垫蛆。 笑死,一個胖子當(dāng)著我的面吹牛腺怯,可吹牛的內(nèi)容都是我干的袱饭。 我是一名探鬼主播,決...
    沈念sama閱讀 41,170評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼呛占,長吁一口氣:“原來是場噩夢啊……” “哼虑乖!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起晾虑,我...
    開封第一講書人閱讀 40,116評論 0 277
  • 序言:老撾萬榮一對情侶失蹤疹味,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后帜篇,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體糙捺,經(jīng)...
    沈念sama閱讀 46,651評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,714評論 3 342
  • 正文 我和宋清朗相戀三年坠狡,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片遂跟。...
    茶點故事閱讀 40,865評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡逃沿,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出幻锁,到底是詐尸還是另有隱情凯亮,我是刑警寧澤,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布哄尔,位于F島的核電站假消,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏岭接。R本人自食惡果不足惜富拗,卻給世界環(huán)境...
    茶點故事閱讀 42,211評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望鸣戴。 院中可真熱鬧啃沪,春花似錦、人聲如沸窄锅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至追驴,卻和暖如春髓帽,著一層夾襖步出監(jiān)牢的瞬間衣盾,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留塌忽,地道東北人。 一個月前我還...
    沈念sama閱讀 49,299評論 3 379
  • 正文 我出身青樓桐经,卻偏偏與公主長得像拓哟,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子河泳,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,870評論 2 361

推薦閱讀更多精彩內(nèi)容