百度地圖導(dǎo)航Flutter插件(iOS端)

navi

前言

在國內(nèi)地圖使用最多的應(yīng)該是高德和百度,對(duì)于Flutter來說高德地圖在兩年前就有了比較成熟方便使用的三方庫。反觀百度地圖,去年上半年的時(shí)候開始推出Flutter版本,但是是手動(dòng)集成的,今年再來看已經(jīng)有了定位、地圖、鷹眼的Flutter版本,而且是通過pub管理的。今年有個(gè)項(xiàng)目只需要百度地圖的導(dǎo)航功能,但是官方恰恰就沒有提供,搜了一下網(wǎng)上好像也沒有相關(guān)的庫。所以就動(dòng)手自己封了一個(gè)簡單的百度導(dǎo)航插件,由于自身以前是iOS開發(fā),所以暫時(shí)只提供了iOS端的插件,Android方面待后續(xù)完善,也希望有此需求的Android開發(fā)能夠幫忙完善。

百度地圖導(dǎo)航iOS端開發(fā)

Swift嘗試

由于平時(shí)開發(fā)中主要是使用Swift,所以一開始創(chuàng)建Flutter插件的時(shí)候就選擇的Swift進(jìn)行開發(fā)。但是在這個(gè)過程中遇到一些問題,主要是庫引入相關(guān)。

  1. 使用pod方式引入BaiduNaviKit,即在podspec中添加導(dǎo)航pod包
s.dependency 'BaiduNaviKit'

問題:百度導(dǎo)航SDK在swift文件中無法通過import BaiduNaviKit的形式引入。在普通的Swift和OC混編工程中,可以通過橋接文件xxx-Bridging-Header.h引入,但是在pod開發(fā)中是不支持橋接文件的。

解決:嘗試通過 https://lingjye.com/2018/06/18/Component-problem/ 中提到的通過設(shè)置private_header_files的方式來充當(dāng)橋接文件

# podspec配置
s.private_header_files = 'Classes/BridgeHeader.h'
// 自定義的頭文件,嘗試充當(dāng)橋接文件,引入導(dǎo)航相關(guān)頭文件
#ifndef BridgeHeader_h
#define BridgeHeader_h

#import "BNaviService.h"
#import <BaiduMapAPI_Base/BMKBaseComponent.h>

#endif /* BridgeHeader_h */

但是pod install之后在umbrella.h中并沒有引入自定義的BridgeHeader.h文件,自然也就沒法使用。不知道哪里沒有設(shè)置正確,如果有pod配置方面比較熟悉的朋友,歡迎提出正確的解決方法。

  1. 使用手動(dòng)引入Framework的方式,如以下配置:
# BaiduNaviSDK/NaviSDK/inc/*.h 引入導(dǎo)航頭文件
s.source_files = 'Classes/**/*', 'BaiduNaviSDK/NaviSDK/inc/*.h'
# 引入導(dǎo)航相關(guān)的.a和.framework包
s.vendored_libraries  = 'BaiduNaviSDK/**/*.a'
s.vendored_frameworks = 'BaiduNaviSDK/**/*.framework'
# 不使用pod依賴,注釋掉
# s.dependency 'BaiduNaviKit'

問題:報(bào)錯(cuò)Include of non-modular header inside framework module 'baidu_map.BridgeHeader,因?yàn)閷?dǎo)航依賴了百度地圖基礎(chǔ)庫,即在NaviSDK中的頭文件中引用了百度地圖的頭文件,如在BNaviService.h中引入了#import <BaiduMapAPI_Map/BMKMapView.h>就會(huì)報(bào)錯(cuò)。

解決:暫時(shí)不知道怎么解決,我猜只有百度官方更新更好的適配Swift才行。

期望

  1. 官方的導(dǎo)航SDK支持import module的形式,可以讓Swift直接調(diào)用
  2. 官方更改目前.h與.a .framework混合的方式,導(dǎo)航SDK統(tǒng)一采用和地圖、定位一樣的純framework方式,滿足手動(dòng)配置的需求

轉(zhuǎn)用OC

因?yàn)樯鲜銮闆r,Swift并不能很方便的接入百度導(dǎo)航。想到OC可以直接引入其頭文件,省去了橋接、庫引用帶來的各種問題,固重新建了以O(shè)C為開發(fā)語言的Flutter插件。

接入方式選擇,pod和Framework手動(dòng)

因?yàn)镺C可以直接引用頭文件,所以直接選擇了pod引入的方式,目前指定了最新的6.2.0版本

s.dependency 'BaiduNaviKit', '6.2.0'

靜態(tài)庫配置問題

加入pod依賴install后,.podspec不做任何額外配置的情況下,還是遇到了一些報(bào)錯(cuò)(警告):

  1. The ‘Pods-XXX‘ target has transitive dependencies that include statically linked binaries

這個(gè)錯(cuò)誤就是百度導(dǎo)航SDK里面用到的.a .framework是靜態(tài)庫的原因

  1. Undefined symbols for architecture arm64

以上兩個(gè)問題可通過配置static_framework = true解決,即

s.static_framework = true

百度導(dǎo)航語音問題

解決庫的引入依賴問題后,參考百度官方的文檔編寫導(dǎo)航相關(guān)的代碼,使用正確的地圖應(yīng)用AppKey授權(quán)導(dǎo)航,即可以正常的在應(yīng)用內(nèi)使用導(dǎo)航功能,授權(quán)代碼調(diào)用authorizeNaviAppKey

但是會(huì)發(fā)現(xiàn)導(dǎo)航?jīng)]有聲音,及時(shí)我在百度AI開放平臺(tái)注冊(cè)了和地圖BundleID一樣的應(yīng)用,同時(shí)也參考官方文檔申請(qǐng)了免費(fèi)額度的語音合成服務(wù)。在導(dǎo)航初始化時(shí)調(diào)用TTS授權(quán)代碼,其回調(diào)的結(jié)果永遠(yuǎn)是NO不成功。

[[BNaviService getInstance] authorizeTTSAppId:ttsAppId apiKey:ttsApiKey secretKey:ttsSecretKey completion:^(BOOL ttsResult) {
        // ttsResult結(jié)果為NO,使用的AI開放平臺(tái)正確的Key
    NSLog(@"authorizeTTS ret = %d",ttsResult);
}];

百度官方的文檔比較老了,語音播報(bào)模塊TTS授權(quán)管理還是http://yuyin.baidu.com/app, 跳轉(zhuǎn)后是到AI開放平臺(tái),其使用說明和文檔的出入還是比較大?,F(xiàn)在的AI開放平臺(tái)功能更復(fù)雜,很多也需要付費(fèi)。我申請(qǐng)了語音模塊,沒有付費(fèi),申請(qǐng)了免費(fèi)額度,不知道是不是這個(gè)原因造成的沒有聲音。為了這個(gè)問題,專門提了工單,建議更新下文檔,提供下解決方案。官方答復(fù)檢查Product Name是否為英文,我檢查了工程是沒問題的。

解決:使用內(nèi)置的TTS語音播報(bào)功能

下載了官方的導(dǎo)航demo,使用自己的BundleID和相關(guān)的Key包括TTS相關(guān)Key,運(yùn)行起來是有導(dǎo)航聲音的,但是斷點(diǎn)查看TTS授權(quán)代碼回調(diào)結(jié)果依舊是NO。

對(duì)比了下兩者的區(qū)別,官方demo是采用手動(dòng)配置SDK的方式,對(duì)比發(fā)現(xiàn)其使用了帶TTS相關(guān)的靜態(tài)庫和資源文件,所以基本可以猜測(cè)是使用的內(nèi)置TTS語音播報(bào)。再去仔細(xì)看了下官方的文檔,第一句話就說明了:使用SDK內(nèi)置百度TTS語音播報(bào)功能需要導(dǎo)入libBNTTSComponentSDK.a靜態(tài)庫,并且需要對(duì)應(yīng)用進(jìn)行授權(quán)驗(yàn)證才能夠使用,因此需要主動(dòng)注冊(cè)應(yīng)用相關(guān)信息。

注:使用內(nèi)置的TTS播報(bào)功能,必須導(dǎo)入libBNTTSComponentSDK.a靜態(tài)庫和baiduTTSSDK.bundle資源文件。而且必須要注冊(cè)TTS相關(guān)的Key,一個(gè)很奇怪的現(xiàn)象是:使用了Key調(diào)用TTS授權(quán)代碼,回調(diào)仍然是NO不成功,但是會(huì)有導(dǎo)航聲音,如果不傳任何的Key,就會(huì)沒有導(dǎo)航聲音。而且我iOS申請(qǐng)的TTS Key拿給Android用也能生效,導(dǎo)航有聲音(包名不同)。所以對(duì)TTS授權(quán)這些Key的實(shí)際使用和相關(guān)準(zhǔn)確配置現(xiàn)在都還有一個(gè)問號(hào)?,F(xiàn)在能保證的是傳入申請(qǐng)的Key,使用內(nèi)置語音奏效!

插件中配置聲音

更新:直接使用pod配置TTS即可,在podspect中添加:

s.dependency 'BaiduNaviKit/TTS'

再次看了下百度導(dǎo)航官方的文檔,發(fā)現(xiàn)TTS是支持pod依賴的,因?yàn)槭亲⑨尩袅怂圆黄鹧郛?dāng)時(shí)沒有看到??,還是自己不夠仔細(xì)啊,走了那么多的彎路

插件中是使用pod的方式接入的導(dǎo)航SDK,里面是沒有內(nèi)置TTS相關(guān)靜態(tài)庫和資源文件的,于是就想到了直接將這兩個(gè)聲音依賴文件引入。將官方下載的libBNTTSComponentSDK.abaiduTTSSDK.bundle拷貝到了插件開發(fā)目錄中(新建了Libs和Resources文件夾存放),并配置了.podspec文件如下:

# pod配置了TTS,手動(dòng)添加的靜態(tài)庫和資源文件就不需要了,刪除即可
# 配置靜態(tài)庫,此處只有l(wèi)ibBNTTSComponentSDK.a
# s.vendored_libraries  = 'Libs/*.a'
# 配置資源文件,此處只有baiduTTSSDK.bundle
# s.resource = ['Resources/*.bundle']

至此,導(dǎo)航有聲音了,整個(gè)插件開發(fā)流程也就圓滿了,再次附上關(guān)鍵的.podspec配置:

# 更新為pod依賴
s.dependency 'BaiduNaviKit', '6.2.0'
s.dependency 'BaiduNaviKit/TTS', '6.2.0'
s.static_framework = true
# s.vendored_libraries  = 'Libs/*.a'    #刪除
# s.resource = ['Resources/*.bundle']   #刪除

開啟Background Modes -> Location updates

注意:根據(jù)官方文檔,在接入自己的應(yīng)用時(shí)不要忘了在Signing&Capabilities中添加Background Mode,選中Location updates,導(dǎo)航是需要后臺(tái)播放更新的,使用過導(dǎo)航的都應(yīng)該知道這個(gè)常識(shí)。如果不設(shè)置的話開始導(dǎo)航時(shí)會(huì)崩潰!

使用

添加依賴

因?yàn)锳ndroid端的功能尚未開發(fā),固還未發(fā)布到pub,目前可使用git source依賴的方式,在pubspec.yaml中配置:

flutter_baidu_navi:
  git: https://github.com/Smiacter/flutter_baidu_navi.git

或下載源碼到本地,使用本地依賴插件的方式,在pubspec.yaml中配置插件目錄路徑

flutter_baidu_navi:
    path: ../plugin/flutter_baidu_navi  # 路勁根據(jù)自己的存放目錄而定

初始化百度地圖定位

/// 在適當(dāng)位置調(diào)用初始化,由于使用內(nèi)置TTS,返回mapSuccess即可
/// 參數(shù)依次為百度地圖AppKey,TTS的AppId、ApiKey、SecretKey
/// TTS相關(guān)的Key為可選參數(shù),如果不傳默認(rèn)為空,導(dǎo)航?jīng)]聲音
FlutterBaiduNavi.init(
  "2GF3O2BTHxYHlMnoEuSFvyLo6c0xBn1h",
  ttsAppId: "24147217",
  ttsApiKey: "vewI9TV5VQRoOGPypStObVL3",
  ttsSecretKey: "QaFknadW5sPlNEicIcrdFV4VeGG0KZvo",
);

初始化返回結(jié)果為BaiduNaviInitResult, 具體可查看源碼

/// 百度導(dǎo)航初始化結(jié)果
enum BaiduNaviInitResult {
  /// 導(dǎo)航Appkey和TTS授權(quán)均成功
  success,
  /// 導(dǎo)航Appkey和TTS授權(quán)均失敗【導(dǎo)航AppKey失敗導(dǎo)航一定會(huì)失敗,且會(huì)彈出未授權(quán)提示框,所以請(qǐng)確保地圖應(yīng)用的AppKey準(zhǔn)確無誤】
  fail,
  /// 導(dǎo)航AppKey成功,TTS失敗
  /// 注:實(shí)踐證明,使用內(nèi)置的TTS語音,有TTS相關(guān)Key,及時(shí)使用這些KeyTTS授權(quán)失敗也會(huì)有導(dǎo)航聲音,而且給包名完全不一樣的Android使用也行!
  /// 如果TTS相關(guān)的Key為空或者不是AI平臺(tái)申請(qǐng)的Key,導(dǎo)航就很有可能沒有聲音
  mapSuccessTtsFail,
  /// 導(dǎo)航AppKey失敗,TTS成功【導(dǎo)航AppKey失敗導(dǎo)航一定會(huì)失敗,且會(huì)彈出未授權(quán)提示框,所以請(qǐng)確保地圖應(yīng)用的AppKey準(zhǔn)確無誤】
  mapFailTtsSuccess,
}

開始導(dǎo)航

/// 開始導(dǎo)航,參數(shù)依次為起點(diǎn)經(jīng)度、起點(diǎn)緯度、終點(diǎn)經(jīng)度、終點(diǎn)緯度
FlutterBaiduNavi.startNavi(
  104.078063,
  30.66664,
  104.122785,
  30.727933,
);

發(fā)起導(dǎo)航返回結(jié)果為BaiduNaviResult, 具體可查看源碼

/// 百度導(dǎo)航結(jié)果
/// 注:選取了一些常見的錯(cuò)誤,如需其他錯(cuò)誤類型,需更改插件代碼返回相應(yīng)的值
enum BaiduNaviResult {
  /// 成功開啟導(dǎo)航
  success,
  /// 定位權(quán)限未開啟或受限
  locationUnauthorized,
  /// 導(dǎo)航服務(wù)未初始化完成
  naviServiceNotInited,
  /// 網(wǎng)絡(luò)不可用
  networkError,
  /// 起點(diǎn)與終點(diǎn)距離太近
  tooNear,
  /// 其他原因-導(dǎo)航失敗
  otherError,
}

TODO:

  • Android端功能開發(fā)
  • 最終完善,發(fā)布到pub

結(jié)尾

以上純粹是個(gè)人的一些嘗試與感悟,并沒有對(duì)百度地圖,pod開發(fā)與配置有太深入的研究,如有什么不對(duì)的地方,還請(qǐng)指出,我下來再專研專研。感謝!附上項(xiàng)目GitHub地址,也歡迎提issue和需求。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容