美文网首页
百度地图导航Flutter插件(iOS端)

百度地图导航Flutter插件(iOS端)

作者: 遗忘的黑鹰 | 来源:发表于2021-05-21 16:46 被阅读0次
    navi

    前言

    在国内地图使用最多的应该是高德和百度,对于Flutter来说高德地图在两年前就有了比较成熟方便使用的三方库。反观百度地图,去年上半年的时候开始推出Flutter版本,但是是手动集成的,今年再来看已经有了定位、地图、鹰眼的Flutter版本,而且是通过pub管理的。今年有个项目只需要百度地图的导航功能,但是官方恰恰就没有提供,搜了一下网上好像也没有相关的库。所以就动手自己封了一个简单的百度导航插件,由于自身以前是iOS开发,所以暂时只提供了iOS端的插件,Android方面待后续完善,也希望有此需求的Android开发能够帮忙完善。

    百度地图导航iOS端开发

    Swift尝试

    由于平时开发中主要是使用Swift,所以一开始创建Flutter插件的时候就选择的Swift进行开发。但是在这个过程中遇到一些问题,主要是库引入相关。

    1. 使用pod方式引入BaiduNaviKit,即在podspec中添加导航pod包
    s.dependency 'BaiduNaviKit'
    

    问题:百度导航SDK在swift文件中无法通过import BaiduNaviKit的形式引入。在普通的Swift和OC混编工程中,可以通过桥接文件xxx-Bridging-Header.h引入,但是在pod开发中是不支持桥接文件的。

    解决:尝试通过 https://lingjye.com/2018/06/18/Component-problem/ 中提到的通过设置private_header_files的方式来充当桥接文件

    # podspec配置
    s.private_header_files = 'Classes/BridgeHeader.h'
    
    // 自定义的头文件,尝试充当桥接文件,引入导航相关头文件
    #ifndef BridgeHeader_h
    #define BridgeHeader_h
    
    #import "BNaviService.h"
    #import <BaiduMapAPI_Base/BMKBaseComponent.h>
    
    #endif /* BridgeHeader_h */
    

    但是pod install之后在umbrella.h中并没有引入自定义的BridgeHeader.h文件,自然也就没法使用。不知道哪里没有设置正确,如果有pod配置方面比较熟悉的朋友,欢迎提出正确的解决方法。

    1. 使用手动引入Framework的方式,如以下配置:
    # BaiduNaviSDK/NaviSDK/inc/*.h 引入导航头文件
    s.source_files = 'Classes/**/*', 'BaiduNaviSDK/NaviSDK/inc/*.h'
    # 引入导航相关的.a和.framework包
    s.vendored_libraries  = 'BaiduNaviSDK/**/*.a'
    s.vendored_frameworks = 'BaiduNaviSDK/**/*.framework'
    # 不使用pod依赖,注释掉
    # s.dependency 'BaiduNaviKit'
    

    问题:报错Include of non-modular header inside framework module 'baidu_map.BridgeHeader,因为导航依赖了百度地图基础库,即在NaviSDK中的头文件中引用了百度地图的头文件,如在BNaviService.h中引入了#import <BaiduMapAPI_Map/BMKMapView.h>就会报错。

    解决:暂时不知道怎么解决,我猜只有百度官方更新更好的适配Swift才行。

    期望

    1. 官方的导航SDK支持import module的形式,可以让Swift直接调用
    2. 官方更改目前.h与.a .framework混合的方式,导航SDK统一采用和地图、定位一样的纯framework方式,满足手动配置的需求

    转用OC

    因为上述情况,Swift并不能很方便的接入百度导航。想到OC可以直接引入其头文件,省去了桥接、库引用带来的各种问题,固重新建了以OC为开发语言的Flutter插件。

    接入方式选择,pod和Framework手动

    因为OC可以直接引用头文件,所以直接选择了pod引入的方式,目前指定了最新的6.2.0版本

    s.dependency 'BaiduNaviKit', '6.2.0'
    

    静态库配置问题

    加入pod依赖install后,.podspec不做任何额外配置的情况下,还是遇到了一些报错(警告):

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

    这个错误就是百度导航SDK里面用到的.a .framework是静态库的原因

    1. Undefined symbols for architecture arm64

    以上两个问题可通过配置static_framework = true解决,即

    s.static_framework = true
    

    百度导航语音问题

    解决库的引入依赖问题后,参考百度官方的文档编写导航相关的代码,使用正确的地图应用AppKey授权导航,即可以正常的在应用内使用导航功能,授权代码调用authorizeNaviAppKey

    但是会发现导航没有声音,及时我在百度AI开放平台注册了和地图BundleID一样的应用,同时也参考官方文档申请了免费额度的语音合成服务。在导航初始化时调用TTS授权代码,其回调的结果永远是NO不成功。

    [[BNaviService getInstance] authorizeTTSAppId:ttsAppId apiKey:ttsApiKey secretKey:ttsSecretKey completion:^(BOOL ttsResult) {
            // ttsResult结果为NO,使用的AI开放平台正确的Key
        NSLog(@"authorizeTTS ret = %d",ttsResult);
    }];
    

    百度官方的文档比较老了,语音播报模块TTS授权管理还是http://yuyin.baidu.com/app, 跳转后是到AI开放平台,其使用说明和文档的出入还是比较大。现在的AI开放平台功能更复杂,很多也需要付费。我申请了语音模块,没有付费,申请了免费额度,不知道是不是这个原因造成的没有声音。为了这个问题,专门提了工单,建议更新下文档,提供下解决方案。官方答复检查Product Name是否为英文,我检查了工程是没问题的。

    解决:使用内置的TTS语音播报功能

    下载了官方的导航demo,使用自己的BundleID和相关的Key包括TTS相关Key,运行起来是有导航声音的,但是断点查看TTS授权代码回调结果依旧是NO

    对比了下两者的区别,官方demo是采用手动配置SDK的方式,对比发现其使用了带TTS相关的静态库和资源文件,所以基本可以猜测是使用的内置TTS语音播报。再去仔细看了下官方的文档,第一句话就说明了:使用SDK内置百度TTS语音播报功能需要导入libBNTTSComponentSDK.a静态库,并且需要对应用进行授权验证才能够使用,因此需要主动注册应用相关信息

    注:使用内置的TTS播报功能,必须导入libBNTTSComponentSDK.a静态库和baiduTTSSDK.bundle资源文件。而且必须要注册TTS相关的Key,一个很奇怪的现象是:使用了Key调用TTS授权代码,回调仍然是NO不成功,但是会有导航声音,如果不传任何的Key,就会没有导航声音。而且我iOS申请的TTS Key拿给Android用也能生效,导航有声音(包名不同)。所以对TTS授权这些Key的实际使用和相关准确配置现在都还有一个问号。现在能保证的是传入申请的Key,使用内置语音奏效!

    插件中配置声音

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

    s.dependency 'BaiduNaviKit/TTS'
    

    再次看了下百度导航官方的文档,发现TTS是支持pod依赖的,因为是注释掉了所以不起眼当时没有看到😂,还是自己不够仔细啊,走了那么多的弯路

    插件中是使用pod的方式接入的导航SDK,里面是没有内置TTS相关静态库和资源文件的,于是就想到了直接将这两个声音依赖文件引入。将官方下载的libBNTTSComponentSDK.abaiduTTSSDK.bundle拷贝到了插件开发目录中(新建了Libs和Resources文件夹存放),并配置了.podspec文件如下:

    # pod配置了TTS,手动添加的静态库和资源文件就不需要了,删除即可
    # 配置静态库,此处只有libBNTTSComponentSDK.a
    # s.vendored_libraries  = 'Libs/*.a'
    # 配置资源文件,此处只有baiduTTSSDK.bundle
    # s.resource = ['Resources/*.bundle']
    

    至此,导航有声音了,整个插件开发流程也就圆满了,再次附上关键的.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

    注意:根据官方文档,在接入自己的应用时不要忘了在Signing&Capabilities中添加Background Mode,选中Location updates,导航是需要后台播放更新的,使用过导航的都应该知道这个常识。如果不设置的话开始导航时会崩溃!

    使用

    添加依赖

    因为Android端的功能尚未开发,固还未发布到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  # 路劲根据自己的存放目录而定
    

    初始化百度地图定位

    /// 在适当位置调用初始化,由于使用内置TTS,返回mapSuccess即可
    /// 参数依次为百度地图AppKey,TTS的AppId、ApiKey、SecretKey
    /// TTS相关的Key为可选参数,如果不传默认为空,导航没声音
    FlutterBaiduNavi.init(
      "2GF3O2BTHxYHlMnoEuSFvyLo6c0xBn1h",
      ttsAppId: "24147217",
      ttsApiKey: "vewI9TV5VQRoOGPypStObVL3",
      ttsSecretKey: "QaFknadW5sPlNEicIcrdFV4VeGG0KZvo",
    );
    

    初始化返回结果为BaiduNaviInitResult, 具体可查看源码

    /// 百度导航初始化结果
    enum BaiduNaviInitResult {
      /// 导航Appkey和TTS授权均成功
      success,
      /// 导航Appkey和TTS授权均失败【导航AppKey失败导航一定会失败,且会弹出未授权提示框,所以请确保地图应用的AppKey准确无误】
      fail,
      /// 导航AppKey成功,TTS失败
      /// 注:实践证明,使用内置的TTS语音,有TTS相关Key,及时使用这些KeyTTS授权失败也会有导航声音,而且给包名完全不一样的Android使用也行!
      /// 如果TTS相关的Key为空或者不是AI平台申请的Key,导航就很有可能没有声音
      mapSuccessTtsFail,
      /// 导航AppKey失败,TTS成功【导航AppKey失败导航一定会失败,且会弹出未授权提示框,所以请确保地图应用的AppKey准确无误】
      mapFailTtsSuccess,
    }
    

    开始导航

    /// 开始导航,参数依次为起点经度、起点纬度、终点经度、终点纬度
    FlutterBaiduNavi.startNavi(
      104.078063,
      30.66664,
      104.122785,
      30.727933,
    );
    

    发起导航返回结果为BaiduNaviResult, 具体可查看源码

    /// 百度导航结果
    /// 注:选取了一些常见的错误,如需其他错误类型,需更改插件代码返回相应的值
    enum BaiduNaviResult {
      /// 成功开启导航
      success,
      /// 定位权限未开启或受限
      locationUnauthorized,
      /// 导航服务未初始化完成
      naviServiceNotInited,
      /// 网络不可用
      networkError,
      /// 起点与终点距离太近
      tooNear,
      /// 其他原因-导航失败
      otherError,
    }
    

    TODO:

    • Android端功能开发
    • 最终完善,发布到pub

    结尾

    以上纯粹是个人的一些尝试与感悟,并没有对百度地图,pod开发与配置有太深入的研究,如有什么不对的地方,还请指出,我下来再专研专研。感谢!附上项目GitHub地址,也欢迎提issue和需求。

    相关文章

      网友评论

          本文标题:百度地图导航Flutter插件(iOS端)

          本文链接:https://www.haomeiwen.com/subject/bewwjltx.html