基于百度地图sdk的地图app开发——导航和模拟导航

Posted 今天也要努力搬砖

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于百度地图sdk的地图app开发——导航和模拟导航相关的知识,希望对你有一定的参考价值。

这是基于百度地图sdk的地图app开发系列博客第七篇

代码仓库位置:https://github.com/YanhuiLu89/lmap.git

上一篇 基于百度地图sdk的地图app开发(六)——路线规划

因为本人是做C++开发,android和java都不熟,这方面知识有说错或者不好的习惯,欢迎赐较。

 

官方参考文档:Android导航SDK

一、工程配置

1.1 在AndroidMainfest.xml中加入以下权限

(指导文档里的权限比这多,前面几个权限在前面几篇文章里,使用百度地图sdk时已经加入了,所以这里只需要加下面这3个就好)

<!--获取Wi-Fi状态,避免产生不必要的网络请求 -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<!-- 用于ugc事件上报拍照录像 -->
<uses-permission android:name="android.permission.CAMERA"/>
<!-- 用于ugc事件上报录音 -->
<uses-permission android:name="android.permission.RECORD_AUDIO"/>

官方文档中授权key配置,在第一篇基于百度地图sdk的地图app开发(一)——开发环境配置与sdk下载里已经做了,所以下面直接到导航SDK核心集成

1.2 导航SDK集成和添加依赖库

基于百度地图sdk的地图app开发(一)——开发环境配置与sdk下载中下载的onsdk_all.aar、和NaviTts.aar两个文件拷贝到app\\libs下,

在build.gradle中添加以下代码

android 
  // apache包
  useLibrary 'org.apache.http.legacy'
  ……

dependencies 
   implementation files('libs\\\\NaviTts.aar')
   implementation files('libs\\\\onsdk_all.aar')
   implementation 'androidx.appcompat:appcompat:1.0.0'
   implementation 'androidx.cardview:cardview:1.0.0'
   implementation 'androidx.recyclerview:recyclerview:1.0.0'
   implementation 'com.android.support.constraint:constraint-layout:1.1.3'
   implementation 'android.arch.lifecycle:extensions:1.1.1'

1.3 TTS授权申请

SDK授权申请后,可以继续申请TTS授权。

    1. 以基于百度地图sdk的地图app开发(一)——开发环境配置与sdk下载中提到的SDK授权申请时相同账号登录 http://yuyin.baidu.com/,点击右上角的“控制台“,进入控制台界面,点击左侧导航栏“语音技术”,然后点击创建应用。

     2. 按步骤填入应用名称、包名等信息。

     3. 点击立即创建会生成App ID,就是在开发中初始化TTS能力时传入的参数之一

至此,和导航SDK集成相关的授权申请就完成了。

虽然做了配置,导航没有声音原因目前未知

 

1.4so库的集成

在第一篇基于百度地图sdk的地图app开发(一)——开发环境配置与sdk下载中已经下载了so库(在arm64-v8a、armeabi和armeabi-v7a文件夹中),并且放置到了app/libs目录下

这里只需要在app的Build.gradle里增加以下红框中设置

另外也可以像官方文档中那样。在 main 目录下创建文件夹 jniLibs (如果有就不需要创建了),将下载文件的 armeabi 等相关文件夹复制到这个目录下,这时候就不需要上面红框的设置了。

1.5 Gradle配置

2为了避免Android "64K 引用限制"引起的异常,在app的build.gradle中需要引入multidex包,并进行相关配置,并且导航SDK内部使用了annotationProcessor,同样需要在build.gradle中配置,如下。

defaultConfig 
    ……
    // 避免"64K 引用限制"
    multiDexEnabled true
    // 导航SDK内部使用了annotationProcessor,需要添加下面代码,防止编译异常
    javaCompileOptions  annotationProcessorOptions  includeCompileClasspath = true  

二、导航SDK初始化

2.1、初始化导航sdk

导航sdk初始化接口详细说明

初始化接口init详细说明如:

/**
 * 初始化百度导航.
 *
 * @param context          建议是应用的context
 * @param sdcardRootPath   系统SD卡根目录路径
 * @param appFolderName    应用在SD卡中的目录名
 * @param naviInitListener 百度导航初始化监听器
 */
void init(final Context context,
          final String sdcardRootPath,
          final String appFolderName,
          final INaviInitListener naviInitListener);

在MainActivity的OnCreate函数中添加以下代码进行初始化

        //导航初始化
        File sdDir = null;
        boolean sdCardExist = Environment.getExternalStorageState()
                .equals(android.os.Environment.MEDIA_MOUNTED);//判断sd卡是否存在
        if(sdCardExist)
        
            sdDir = Environment.getExternalStorageDirectory();//获取跟目录
        
        BaiduNaviManagerFactory.getBaiduNaviManager().init(getApplicationContext(), sdDir.toString(), "lmap",
                new IBaiduNaviManager.INaviInitListener() 
                      @Override
                    public void onAuthResult(int i, String s) 
                        if(i==0)
                        
                            Toast.makeText(MainActivity.this, "key校验成功!", Toast.LENGTH_SHORT).show();
                        
                        else if(i==1)
                        
                            Toast.makeText(MainActivity.this, "key校验失败, " + s, Toast.LENGTH_SHORT).show();
                        
                    

                    @Override
                    public void initStart() 

                    

                    @Override
                    public void initSuccess() 
                        Toast.makeText(MainActivity.this, "百度导航引擎初始化成功", Toast.LENGTH_SHORT).show();
                        // 初始化tts
                        initTTs();

                    

                    @Override
                    public void initFailed(int i) 
                        Toast.makeText(MainActivity.this, "百度导航引擎初始化失败", Toast.LENGTH_SHORT).show();

                    
                );

2.2 初始化tts

增加函数initTTs(),其调用时机为导航sdk初始化成功时调用,见上图initSuccess()接口中,其函数中先初始化,再 注册同步内置tts状态回调

    private void initTTs() 
        File sdDir = null;
        boolean sdCardExist = Environment.getExternalStorageState()
                .equals(android.os.Environment.MEDIA_MOUNTED);//判断sd卡是否存在
        if(sdCardExist)
        
            sdDir = Environment.getExternalStorageDirectory();//获取跟目录
        
        BNTTsInitConfig.Builder builder=new BNTTsInitConfig.Builder();
        builder.context(getApplicationContext()).sdcardRootPath(sdDir.toString()).appFolderName("lmap").appId(("填入1.3步骤申请的app id"));
        BaiduNaviManagerFactory.getTTSManager().initTTS( builder.build());

        // 注册同步内置tts状态回调
        BaiduNaviManagerFactory.getTTSManager().setOnTTSStateChangedListener(
                new IBNTTSManager.IOnTTSPlayStateChangedListener() 
                    @Override
                    public void onPlayStart() 
                        Log.e("lmap", "ttsCallback.onPlayStart");
                    

                    @Override
                    public void onPlayEnd(String speechId) 
                        Log.e("lmap", "ttsCallback.onPlayEnd");
                    

                    @Override
                    public void onPlayError(int code, String message) 
                        Log.e("lmap", "ttsCallback.onPlayError");
                    
                
        );
    

三、增加导航功能入口

完成了一二步的配置和初始化,就可以开始使用导航功能了

3.1增加开始导航和模拟导航按钮

增加开始导航和模拟导航两个按钮,其布局代码如下

  <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="90dp"
        android:layout_marginTop="630dp"
        android:orientation="horizontal">
        <Button
            android:id="@+id/startnavi"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:background="@color/banv_poi_search_text"
            android:text="开始导航"
            android:textSize="30sp"
            android:visibility="gone" />

        <Button
            android:id="@+id/simnavi"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:background="@color/banv_poi_search_text"
            android:text="模拟导航"
            android:textSize="30sp"
            android:visibility="gone" />
    </LinearLayout>

在上一章基于百度地图sdk的地图app开发(六)——路线规划,算路完成描画路线后,设置按钮可见,即在onGetDrivingRouteResult函数最后,添加以下代码

                    //显示开始导航按钮
                    Button startNav=findViewById(R.id.startnavi);
                    startNav.setVisibility(View.VISIBLE);
                    Button simNav=findViewById(R.id.simnavi);
                    simNav.setVisibility(View.VISIBLE);

算路完成显示效果如下

 

3.2开始导航和模拟导航按钮的响应函数中启动导航

我的本意是想使用上一章 基于百度地图sdk的地图app开发(六)——路线规划 算出来的路线结果DrivingRouteResult,来进行导航。客户点击选择哪条路线,就把哪条路线传给导航,开始导航。(我之前的开发经验大部分都是这样。)但是看IBNRoutePlanManager只接受点再重新算路,开始导航,并不接受DrivingRouteResult。所以只能画路线前,保存下来目的地,点击开始导航时再重新算一次。

实现步骤,在MainActivity中增加成员变量mDestation

private PoiInfo mDestation=null;

增加设置mDestation的接口

 public void setDestation(PoiInfo poi)mDestation=poi;

在 上一章基于百度地图sdk的地图app开发(六)——路线规划提到的去这里按钮,点击函数中调用设置mDestation的接口

最后在开始导航按钮的click函数调用startNavi参数传true即启动真实导航,在模拟导航按钮的响应函数中调用startNavi,参数传false,即启动模拟导航。代码如下

     //增加开始导航按钮的响应
        Button startNav=findViewById(R.id.startnavi);
        startNav.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View v) 
                startNavi(true);
            
        );
        Button simNav=findViewById(R.id.simnavi);
        simNav.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View v) 
                startNavi(false);
            
        );

startNavi接口的实现如下

        private void startNavi(boolean isRealNavi) 
        if(mCurLocation==null||mDestation==null)
            return;
        BNRoutePlanNode sNode = new BNRoutePlanNode.Builder()
                .latitude(mCurLocation.getLatitude())
                .longitude(mCurLocation.getLongitude())
                .name("我的位置")
                .description("我的位置")
                .build();
        BNRoutePlanNode eNode = new BNRoutePlanNode.Builder()
                .latitude(mDestation.getLocation().latitude)
                .longitude(mDestation.getLocation().longitude)
                .name(mDestation.name)
                .description(mDestation.name)
                .build();
        List<BNRoutePlanNode> list = new ArrayList<>();
        list.add(sNode);
        list.add(eNode);
        BaiduNaviManagerFactory.getRoutePlanManager().routePlanToNavi(
                list,
                IBNRoutePlanManager.RoutePlanPreference.ROUTE_PLAN_PREFERENCE_DEFAULT,
                null,
                new Handler(Looper.getMainLooper()) 
                    @Override
                    public void handleMessage(Message msg) 
                        switch (msg.what) 
                            case IBNRoutePlanManager.MSG_NAVI_ROUTE_PLAN_START:
                                Toast.makeText(MainActivity.this.getApplicationContext(),
                                        "算路开始", Toast.LENGTH_SHORT).show();
                                break;
                            case IBNRoutePlanManager.MSG_NAVI_ROUTE_PLAN_SUCCESS:
                                Toast.makeText(MainActivity.this.getApplicationContext(),
                                        "算路成功", Toast.LENGTH_SHORT).show();
                                break;
                            case IBNRoutePlanManager.MSG_NAVI_ROUTE_PLAN_FAILED:
                                Toast.makeText(MainActivity.this.getApplicationContext(),
                                        "算路失败", Toast.LENGTH_SHORT).show();
                                break;
                            case IBNRoutePlanManager.MSG_NAVI_ROUTE_PLAN_TO_NAVI:
                                Toast.makeText(MainActivity.this.getApplicationContext(),
                                        "算路成功准备进入导航", Toast.LENGTH_SHORT).show();
                                Intent intent = new Intent(MainActivity.this,
                                        DemoGuideActivity.class);
                                intent.putExtra("isRealNavi",isRealNavi);
                                startActivity(intent);
                                break;
                            default:
                                // nothing
                                break;
                        
                    

                );
    

以上“算路成功准备进入导航”用到的DemoGuideActivity在第4节部分实现

四、DemoGuideActivity的实现

新增Activity,继承自FragmentActivity(这一点很重要,如果按照默认继承自AppCompatActivity,在调用BaiduNaviManagerFactory.getRouteGuideManager().onCreate(this, config)会崩溃)。

4.1管理百度专业导航生命周期

在MainActivity的onCreate中调用RouteGuideManager的onCreate函数并将返回的view,设置成MainActivity的ContentView,代码如下

        //管理专业导航生命周期
        Bundle bundle = new Bundle();
        // IS_REALNAVI代表导航类型,true表示真实导航,false表示模拟导航,默认是true
        boolean isRealNavi=getIntent().getBooleanExtra("isRealNavi",true);
        bundle.putBoolean(BNaviCommonParams.ProGuideKey.IS_REALNAVI,isRealNavi);
        // IS_SUPPORT_FULL_SCREEN代表是否沉浸式,默认是true
        bundle.putBoolean(BNaviCommonParams.ProGuideKey.IS_SUPPORT_FULL_SCREEN, true);
        BNGuideConfig config = new BNGuideConfig.Builder().params(bundle).build();
        View view=BaiduNaviManagerFactory.getRouteGuideManager().onCreate(this, config);
        if (view != null) 
            setContentView(view);
        

在onStart、onResume、onPause、onStop、onDestroy接口中,分别增加以下代码 

 BaiduNaviManagerFactory.getRouteGuideManager().onStart();
BaiduNaviManagerFactory.getRouteGuideManager().onResume();
 BaiduNaviManagerFactory.getRouteGuideManager().onPause();
BaiduNaviManagerFactory.getRouteGuideManager().onStop();
 BaiduNaviManagerFactory.getRouteGuideManager().onDestroy(false);

4.2设置导航监听,增加结束导航处理

设置导航事件监听,在onNaviGuideEnd()中结束当前activity代码如下,其余接口暂时空实现
 //设置导航事件监听
        BaiduNaviManagerFactory.getRouteGuideManager().setNaviListener(new IBNaviListener() 
           //..其余一些空实现接口
            @Override
            public void onNaviGuideEnd() 
                finish();

            
        );

设置导航view监听,在onNaviBackClick()内终止导航,其余接口空实现

 //设置导航视图监听
        BaiduNaviManagerFactory.getRouteGuideManager().setNaviViewListener(new IBNaviViewListener() 

           //。。。。一些空实现接口。。。。

            @Override
            public void onNaviBackClick() 
                BaiduNaviManagerFactory.getRouteGuideManager().stopNavi();
            

          //。。。一些空实现接口。。。。。
        );

程序效果,以下分别为真实导航、模拟导航、退出导航的效果

             

五、解决遇到的问题

5.1 jni接口找不到实现No implementation found for......

错误log如下

No implementation found for boolean com.baidu.navisdk.jni.nativeif.JNIGuidanceControl.SetRotateMode(int) (tried Java_com_baidu_navisdk_jni_nativeif_JNIGuidanceControl_SetRotateMode and Java_com_baidu_navisdk_jni_nativeif_JNIGuidanceControl_SetRotateMode__I)
        at com.baidu.navisdk.jni.nativeif.JNIGuidanceControl.SetRotateMode(Native Method)

如果集成so库没有问题,出现这个问题多半是要更新库,我这里是更新库之后ok.

5.2 BaiduNaviManagerFactory.getRouteGuideManager().onCreate(this, config)崩溃

崩溃log如下

Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.View android.view.ViewGroup.findViewById(int)' on a null object reference

原因:DemoGuideActivity不能继承自AppCompatActivity

修改:改成public class DemoGuideActivity extends FragmentActivity ,崩溃问题解决

 

六、遗留问题没有声音

导航没有声音

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

以上是关于基于百度地图sdk的地图app开发——导航和模拟导航的主要内容,如果未能解决你的问题,请参考以下文章

基于百度地图sdk的地图app开发——路线规划

基于百度地图sdk的地图app开发——路线规划

基于百度地图sdk的地图app开发——poi检索

android开发如何用高德地图进行模拟定位.

ReactNative 调用手机地图(高德百度)导航 Android

使用百度地图API进行Android地图应用开发(Eclipse)