Android导航功能实现:调起高德/百度地图与集成SDK全指南
发布时间:2026/9/7 7:48:03 锦皓数字建站

简介面向Android开发者的百度地图导航功能实现资源围绕SDK集成、用户定位、起终点设置、路线规划与导航引导展开适合需要在地图类App中快速加入导航能力的技术人员参考。压缩包共1343个文件以xml布局配置、png图标、json数据、class与jar依赖为主并附有可安装的apk示例整体大小22.55MB已有514人学习下载。实用价值在于提供了完整工程参考从AndroidManifest权限声明、百度地图API密钥配置到LocationClient回调处理、RoutePlanSearch路线检索、Overlay图层绘制再到TextToSpeech语音提示与地图视角动态更新均有对应实现可对照查看。包内既保留了可运行的apk版本也有中间构建产物和资源文件便于开发者直接导入工程分析源码结构理解导航功能从位置获取到路线展示、再到实时引导的完整流程。对于希望从零搭建导航能力或优化现有定位路线的开发者具备较高的实操参考价值。 Android 导航功能开发很多新手拿到需求第一反应是“我要自己写路径规划算法”这是最大的误区。导航的本质是“定位 地图展示 路径计算 引导”这四个环节里除了定位需要你自己处理一部分逻辑其余全部可以交给成熟的地图服务商。这篇内容我会从一个真实项目的角度把 Android 导航功能从方案选型到落地实现的完整链路讲清楚包括两种最主流的实现方式、核心代码、以及文档里不会写清楚的坑。1. 方案选型为什么我不建议你自己写路径规划先聊聊我踩过的坑。早期我做了一个物流配送 App当时老板说“我们要做导航”我第一版方案是自己用 MapView 画线然后请求第三方服务商返回的经纬度点集自己去算距离、算偏航、算剩余时间。做完以后发现两个致命问题一是路径计算的精度和实时路况完全跟不上二是耗电和流量非常夸张。后来老老实实改用成熟的导航 SDK整个功能从两周缩到三天。Android 导航功能目前业内主流的落地方案只有三种方案适用场景开发成本维护成本调起第三方地图 App高德/百度/腾讯不要求 App 内嵌地图跳转到外部导航极低极低集成地图 SDK 并调用导航组件App 内完成导航不希望跳出中中完全自研地图与导航暂无除非你是地图厂商极高极高我个人的建议是如果你的业务对“导航”的要求只是从 A 点到 B 点直接调起系统里已安装的第三方地图这是最省事也最稳妥的方式。但如果你的产品需要把导航过程嵌入到自己 App 的流程中比如接单后司机必须在 App 内完成导航那就必须集成高德或者百度这类地图 SDK。另外要说清楚一点所谓的“Android 实现导航功能”本质上不是“你实现了导航”而是“你在自己的应用里把用户引导到了导航能力上”。用户真正需要的是一段可靠的导航体验而不是你的 App 里多了一个画着路线的地图。2. 快速落地通过 Intent 调起高德/百度地图进行导航如果你的需求只是“点击按钮调起地图导航”那完全不需要引入任何 SDK。Android 系统允许通过隐式 Intent 唤起指定包名的地图 App并传递起始点和终点经纬度。2.1 调起高德地图导航高德地图的唤起协议比较简单核心是构造一个uri格式的 Intent。示例代码如下public void startAmapNavi(Context context, double startLat, double startLng, double endLat, double endLng) { try { StringBuilder builder new StringBuilder(); builder.append(androidamap://navi?sourceApplication).append(myapp); builder.append(poiname).append(Uri.encode(目的地)); builder.append(lat).append(endLat); builder.append(lon).append(endLng); builder.append(dev1); builder.append(style2); Intent intent new Intent(android.intent.action.VIEW); intent.setData(Uri.parse(builder.toString())); // 高德地图的包名 intent.setPackage(com.autonavi.minimap); context.startActivity(intent); } catch (Exception e) { Toast.makeText(context, 未检测到高德地图, Toast.LENGTH_SHORT).show(); } }需要注意一点androidamap://navi这个协议不带起点时高德默认从“当前位置”开始。style参数表示导航方式2是驾车导航3是公交4是步行。不同的业务场景需要按需调整。2.2 调起百度地图导航百度地图的唤起方式类似不过协议格式不太一样public void startBaiduNavi(Context context, double startLat, double startLng, double endLat, double endLng) { try { // 百度地图使用的经纬度需要是百度坐标系BD-09 double[] baiduEnd wgs84ToBd09(endLat, endLng); StringBuilder builder new StringBuilder(); builder.append(baidumap://map/direction); builder.append(?destinationlatlng:).append(baiduEnd[0]).append(,) .append(baiduEnd[1]); builder.append(|name:).append(Uri.encode(目的地)); builder.append(modedriving); builder.append(coord_typebd09ll); builder.append(srcyourAppName); Intent intent new Intent(android.intent.action.VIEW); intent.setData(Uri.parse(builder.toString())); intent.setPackage(com.baidu.BaiduMap); context.startActivity(intent); } catch (Exception e) { Toast.makeText(context, 未检测到百度地图, Toast.LENGTH_SHORT).show(); } }这里有一个很多人不知道的坑百度地图的坐标体系是百度自有坐标系BD-09高德是国测局坐标系GCJ-02GPS 拿到的是 WGS-84。如果直接把 GPS 的经纬度传给百度大概率会偏移几十到几百米这在步行导航时非常致命。所以调百度之前必须先做一次坐标转换下面的方法可以直接用public static double[] wgs84ToBd09(double lat, double lon) { // WGS-84 转 GCJ-02 double[] gcj wgs84ToGcj02(lat, lon); double x gcj[1]; double y gcj[0]; double z Math.sqrt(x * x y * y) 0.00002 * Math.sin(y * Math.PI * 3000.0 / 180.0); double theta Math.atan2(y, x) 0.000003 * Math.cos(x * Math.PI * 3000.0 / 180.0); double bdLon z * Math.cos(theta) 0.0065; double bdLat z * Math.sin(theta) 0.006; return new double[]{bdLat, bdLon}; }而高德地图自身支持 GCJ-02GPS 坐标传入前同样需要转换但它的 SDK 内部有些接口已经帮你转好了这点后面会说。2.3 调起前要做的检查调起第三方地图之前至少要判断目标 App 是否安装。不要只靠try-catch因为有些国产 ROM 上startActivity不一定会抛ActivityNotFoundException而是静默失败。更稳的方案是检查包名是否存在public static boolean isAppInstalled(Context context, String packageName) { PackageManager pm context.getPackageManager(); try { pm.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES); return true; } catch (PackageManager.NameNotFoundException e) { return false; } }如果没安装可以弹 Dialog 引导用户去应用商店搜索下载或者直接跳转到应用市场详情页。实际项目中建议做成一个统一的“可选择地图列表”检测到高德就显示高德检测到百度就显示百度两个都没有就提示下载。3. 应用内导航集成高德地图 SDK 的完整过程如果你确定要做 App 内嵌导航那不要三个地图都接选一个主力就够了。我做项目时最终选了高德主要原因是它的驾乘导航 SDK 对路径规划和导航状态回调的支持更完整文档也相对清楚。当然你选百度也行整体流程是类似的只是 API 名字不同。3.1 配置工程最容易卡住的环节高德 SDK 的接入需要几个步骤网上的旧教程很容易误导人。现在的正确做法是在build.gradle的dependencies中引入导航 SDK 和相关组件dependencies { implementation com.amap.api:navi-3dmap:9.7.1 implementation com.amap.api:search:9.7.1 implementation com.amap.api:location:6.4.7 }在AndroidManifest.xml里声明权限uses-permission android:nameandroid.permission.INTERNET / uses-permission android:nameandroid.permission.ACCESS_COARSE_LOCATION / uses-permission android:nameandroid.permission.ACCESS_FINE_LOCATION / uses-permission android:nameandroid.permission.ACCESS_WIFI_STATE / uses-permission android:nameandroid.permission.CHANGE_WIFI_STATE / uses-permission android:nameandroid.permission.ACCESS_NETWORK_STATE / uses-permission android:nameandroid.permission.WAKE_LOCK / uses-permission android:nameandroid.permission.WRITE_EXTERNAL_STORAGE /在 Application 的onCreate里设置 API Keypublic class MyApplication extends Application { Override public void onCreate() { super.onCreate(); AMapNavi.setApiKey(你的 Key); // 如果你也用了定位和地图也要分别 setApiKey } }这里有个核心坑高德的导航 SDK、地图 SDK、定位 SDK 是三个不同的 Key 类型。你在官网申请 Key 时如果选错了类型比如选了“地图”但代码里用的是“导航”运行时不会报错但地图会白屏或者导航回调不触发。排查这类问题时先确认 Key 类型和包名/SHA1 是否完全匹配。3.2 初始化导航与监听事件导航 SDK 的核心类是AMapNavi使用前先初始化private AMapNavi mAmapNavi; private void initNavi() { mAmapNavi AMapNavi.getInstance(getApplicationContext()); mAmapNavi.addAMapNaviListener(new AMapNaviListener() { Override public void onInitNaviFailure() { // 初始化失败一般是 Key 不对或者缺少权限 } Override public void onInitNaviSuccess() { // 初始化成功可以开始算路 calculateRoute(); } }); }初始化成功后需要调用算路接口。高德算路分驾车calculateDriveRoute、步行calculateWalkRoute和骑行calculateRideRoute三类应用内导航通常以驾车为主。3.3 算路成功后的导航控制算路成功的回调里会返回路径信息NaviPath你可以从中拿到路径总长度、预计时间、路径坐标点集等Override public void onCalculateRouteSuccess(int[] routeIds) { NaviPath naviPath mAmapNavi.getNaviPath(); if (naviPath ! null) { long totalDistance naviPath.getAllLength(); // 米 long totalTime naviPath.getAllTime(); // 秒 } // 启动实时导航 mAmapNavi.startNavi(AMapNavi.EmulatorNaviMode); }startNavi有两种模式EmulatorNaviMode是模拟导航GPSNaviMode是真实导航。调试阶段建议先用模拟导航它会沿着路径自动模拟位置变化方便你测试 UI 和状态流转不会因为没走出去而无法验证。导航启动后还需要把AMapNaviView添加到界面上。这个 View 是导航 SDK 提供的地图控件它内部集成了地图渲染、导航箭头、路线高亮和路况图层。实际项目中你把AMapNaviView放进 XML 布局然后在 Activity 的生命周期方法里对应调用它的onResume、onPause、onDestroy否则会出现内存泄漏或者导航图层不刷新的问题。4. GPS 定位与坐标纠偏导航准确性的关键导航功能绕不开定位。很多新手在接入第三方的定位 SDK 之后发现坐标漂移、位置跳变这是因为定位 SDK 返回的坐标类型和地图/导航期望的坐标类型不一致。高德 SDK 内部默认使用 GCJ-02所以如果你用自己写的LocationManager拿原始 GPS 坐标直接传入导航算路接口就会偏。4.1 用高德定位 SDK 避免坐标系混乱最省事的办法是用高德自己的定位 SDK它返回的AMapLocation已经是 GCJ-02 坐标可以直接用于导航AMapLocationClient locationClient new AMapLocationClient(context); AMapLocationClientOption option new AMapLocationClientOption(); option.setLocationMode(AMapLocationClientOption.AMapLocationMode.High_Accuracy); option.setOnceLocation(false); option.setInterval(2000); locationClient.setLocationOption(option); locationClient.setLocationListener(aMapLocation - { if (aMapLocation.getErrorCode() 0) { double lat aMapLocation.getLatitude(); double lng aMapLocation.getLongitude(); // 这里拿到的就是 GCJ-02 坐标直接用 } else { // 定位失败看错误码 } }); locationClient.startLocation();High_Accuracy模式会同时使用 GPS、Wi-Fi 和基站定位在室内也能返回一个大概位置。实测在写字楼靠窗的位置能定位到在地下室就只能靠基站了误差可能到几百米这是硬件与环境的物理限制不是代码问题。4.2 定位权限的动态申请Android 6.0 以上需要动态申请定位权限。很多人只申请了ACCESS_FINE_LOCATION忽略了ACCESS_COARSE_LOCATION或者反过来。实际调试导航时发现只申请精确权限在某些机型上反而会定位失败。建议两个都申请并且要在onRequestPermissionsResult里处理拒绝后的降级逻辑if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) ! PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{ Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION }, 1001); }这里有一个用户体验上的细节如果用户第一次点了“拒绝”下次再弹窗时系统会勾选“不再询问”之后requestPermissions不会再弹窗会直接回调denied。所以如果发现权限被永久拒绝最好引导用户去设置页打开否则导航永远定位不了。4.3 模拟定位与真机差异调试导航时Android 模拟器的定位设置往往不准。Android Studio 自带的 Emulator 可以手动设置经纬度但精度和响应速度很差我很少用它来测导航。推荐两个方案用真机加模拟定位 App如“Fake GPS”把手机位置设成一个固定点然后人不用动就能全程观察导航状态。直接用高德导航 SDK 的EmulatorNaviMode模拟导航它连运动轨迹都帮你模拟好了强烈建议第一次跑通功能用它。注意模拟导航和真实导航在状态回调上会有细微区别。模拟导航的onLocationChange回调频率更高而且没有漂移真实导航在隧道和高架桥下会有信号遮挡onLocationChange会变慢甚至短暂停止。测试时要两者都覆盖。5. 导航状态处理偏航、到达与多路线场景导航不是“算出路线就完事”实际使用时一定会遇到偏航、堵车改路线、信号丢失等情况。导航 SDK 提供了一些状态回调你要在业务层把它们接住并做对应处理。5.1 偏航重算逻辑高德导航 SDK 会在偏航时回调onReCalculateRouteForYaw。默认情况下 SDK 会自动重新算路但如果你没有处理可能会导致路线频繁跳变。建议在这个回调里加一个冷却时间比如 3 秒内只允许重算一次防止在复杂立交桥路段反复刷新路线private long lastRecalcTime 0; Override public void onReCalculateRouteForYaw() { long currentTime System.currentTimeMillis(); if (currentTime - lastRecalcTime 3000) { lastRecalcTime currentTime; // 重新算路或者弹 Toast 提示 } }同理onReCalculateRouteForTrafficJam是交通拥堵后自动重算路径的回调你可以在里面做“当前路线拥堵已为您重新规划”的提示。5.2 到达与导航结束导航结束的回调是onArrivedDestination。到达后务必要做三件事停止播报stopSpeak、停止导航stopNavi、销毁界面AMapNaviView.onDestroy。否则会出现一个很典型的问题用户到达目的地后App 后台还在持续定位和播报耗电严重。5.3 多路线选择高德算路接口支持最多 3 条路线// 起点 ListNaviLatLng startList new ArrayList(); startList.add(new NaviLatLng(startLat, startLng)); // 途经点可选 ListNaviLatLng wayPointList new ArrayList(); // 终点 ListNaviLatLng endList new ArrayList(); endList.add(new NaviLatLng(endLat, endLng)); int strategy 0; // strategy 0 表示速度优先1 表示费用优先2 表示距离优先 // 也可以组合比如 4 表示不走高速 mAmapNavi.calculateDriveRoute(startList, endList, wayPointList, strategy);算路成功后routeIds数组长度可能大于 1你可以通过mAmapNavi.getNaviPath(routeIds[i])分别获取每条路线的信息在 UI 上做成“推荐方案/备选方案”的列表让用户自己选。我个人建议不要给用户太多选择默认用推荐路线即可。实测数据表明 90% 以上用户不会主动切换路线反而是“切换路线”按钮在某些定制 ROM 上占了导航界面空间影响驾驶安全。6. 上线前必须注意的几个隐藏问题最后谈几个我实际开发中踩过的坑这些在官方文档里不会写得很详细但遇到任何一个都够你排查大半天。6.1 Android 9 以上 HTTP 明文流量限制高德导航 SDK 的部分服务请求是 HTTP 的Android 9API 28开始默认禁止明文流量如果你不处理地图瓦片和路况数据会加载不出来界面一片空白。在AndroidManifest.xml的application标签里加一行android:usesCleartextTraffictrue或者更规范的做法是配置networkSecurityConfig只允许高德相关域名使用 HTTP。如果只图省事上面那行就够了。6.2 混淆配置如果开启代码混淆必须给高德 SDK 加上 keep 规则否则线上会崩在奇怪的位置。到官网 SDK 下载页的“开发文档”里找混淆配置模板直接复制粘贴到你的proguard-rules.pro。一个典型的坑是Debug 包一切正常Release 包一进入导航界面就闪退十有八九是混淆没配好。6.3 导航界面横竖屏锁定导航界面最好锁定为竖屏尤其是驾车场景下。一是横屏切换会导致 Activity 重建导航状态丢失二是很多车辆的蓝牙系统在横屏时不方便操作。在AndroidManifest.xml里给导航 Activity 加android:screenOrientationportrait6.4 原生的系统返回键处理导航进行中用户一旦点系统返回键退出导航页你需要确认导航真正被销毁了。最简单的处理是在导航页的onBackPressed里加一个二次确认Override public void onBackPressed() { if (mAmapNavi ! null mAmapNavi.isGpsNavi()) { new AlertDialog.Builder(this) .setMessage(导航正在进行确定退出吗) .setPositiveButton(退出, (d, w) - { stopNavi(); finish(); }) .setNegativeButton(取消, null) .show(); } else { super.onBackPressed(); } }关于导航功能其实真正影响体验的不是“能不能导航”而是“导航中遇到各种意外情况时你的 App 是否稳定”。方案选型、坐标转换、生命周期管理、状态回调处理这几件事做好导航功能就算立住了。剩下的就是大量的真机测试——尤其是隧道、高架、地下停车场这些场景别偷懒多跑几趟。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。