
简介这是一份面向Android开发者的导航功能实现参考工程围绕百度地图API构建从当前位置到目的地的完整导航链路适合具备一定Android基础、希望掌握地图集成与路线规划的中级开发者学习。资源包共1343个文件约22.55MB以525个xml布局与配置、498个png图片资源、129个json数据、96个class与36个jar依赖库为主另含少量java源码、aidl接口及gradle构建脚本完整保留了工程结构与依赖关系。内容覆盖百度地图SDK集成、权限配置、LocationClient定位、起点终点标记、RoutePlanSearch路线规划以及导航引导与语音提示等关键环节可帮助读者理解地图导航模块的组织方式与调用流程。目前已有518人学习下载适合作为课程设计、毕业设计或功能原型开发的参考素材便于对照源码梳理实现思路、排查集成问题并快速搭建可运行的导航Demo。1. 导航功能不是调个 API 就完事从一次定位漂移说起去年帮一个做同城配送的团队看代码他们的骑手端在市区主干道上画出的轨迹像心电图每隔几百米就往外甩出几十米。开发同学第一反应是“高德/百度 SDK 的锅”换了三家地图厂商问题照旧。最后定位到根因他们把LocationClient的定位模式设成了高精度却没做坐标系转换GPS 原始坐标直接喂给了只认 GCJ-02 的地图渲染层再加上没有做轨迹抽稀和卡尔曼滤波漂移被放大成了“骑手在河里游泳”。这就是 Android 导航功能的真实门槛——它从来不是startNavigation()一行调用的事。一套能上线的导航能力至少要串起定位采集、坐标系对齐、路径规划、导航状态机、偏航重算、后台保活这六个环节任何一环偷懒用户看到的就是“箭头乱飘、语音延迟、切后台就断”。这份资源包围绕的正是这条完整链路适合两类人一是刚接手 LBS 模块、想照着把 Demo 跑通的初中级 Android 开发二是被线上导航投诉折磨过、想回头补坐标系和状态机这一课的熟手。下面按“先跑通、再抠参数、最后避坑”的顺序拆。2. 定位与坐标系导航功能的地基怎么打2.1 三种定位模式与选型逻辑Android 侧拿位置有三条路系统LocationManager、Google 的FusedLocationProviderClient、以及国内地图 SDK 自带的定位客户端。资源包里默认走的是地图 SDK 的定位模块原因是它把 GPS、基站、WiFi、传感器做了融合并且直接吐出国测加密后的坐标省掉一次手动转换。选型时看三个参数LocationMode、interval、needAddress。LocationMode分高精度、低功耗、仅设备三档。导航场景必须用高精度但高精度不等于一直开 GPS——SDK 内部会在信号差时切基站你要做的是把scanSpan设成 1000ms 到 2000ms 之间。设成 500ms 以下耗电曲线陡增收益却很小因为民用 GPS 的更新率本身就在 1Hz 左右。// 定位客户端初始化注意 scanSpan 与定位模式的配合 LocationClient locationClient new LocationClient(context); LocationClientOption option new LocationClientOption(); option.setLocationMode(LocationClientOption.LocationMode.Hight_Accuracy); option.setScanSpan(1500); // 1.5 秒一次兼顾流畅与功耗 option.setNeedAddress(true); // 导航播报需要逆地理名称 option.setCoorType(gcj02); // 关键直接要国测局坐标 option.setOpenGps(true); option.setLocationNotify(true); // 位置变化主动回调 locationClient.setLocOption(option);这段代码里最容易被忽略的是setCoorType。如果你后续用高德渲染就填gcj02用百度渲染填bd09ll如果要做跨端轨迹比对统一存wgs84再在展示层转。参数说明scanSpan是回调间隔不是定位耗时setLocationNotify(true)会让 SDK 在位置变化时主动推而不是等你轮询。2.2 坐标系转换一次写对省掉三天排查国内地图生态有三套坐标WGS-84GPS 原始、GCJ-02国测局加密、BD-09百度在 GCJ 上二次偏移。资源包里带了一个CoordConverter工具类核心是两段偏移算法。很多人图省事直接调地图 SDK 的转换接口但离线场景或批量处理轨迹时本地算法更稳。// WGS-84 转 GCJ-02 的核心偏移完整版在资源包 CoordConverter.java private static double transformLat(double x, double y) { double ret -100.0 2.0 * x 3.0 * y 0.2 * y * y 0.1 * x * y 0.2 * Math.sqrt(Math.abs(x)); ret (20.0 * Math.sin(6.0 * x * Math.PI) 20.0 * Math.sin(2.0 * x * Math.PI)) * 2.0 / 3.0; return ret; } // 调用时先判断是否在国内范围境外坐标不做偏移 public static double[] wgs84ToGcj02(double lat, double lon) { if (outOfChina(lat, lon)) return new double[]{lat, lon}; double dLat transformLat(lon - 105.0, lat - 35.0); double dLon transformLon(lon - 105.0, lat - 35.0); // ... 省略弧度换算与偏移量计算见资源包 }逻辑说明outOfChina是边界判断经度 73.66~135.05、纬度 3.86~53.55 之外直接返回原值否则会出现境外坐标被错误偏移的玄学问题。参数上transformLat里的系数是经验拟合值不要自己改。转换精度在米级导航够用如果做车道级需要更高阶的拟合。2.3 轨迹平滑别让原始点直接上屏定位回调拿到的点带噪声直接连成线就是锯齿。资源包里用了滑动平均加距离阈值抽稀两点距离小于 5 米就丢弃连续点做三点加权平均。这一步不做后面路径匹配会频繁触发偏航。// 简易轨迹平滑距离抽稀 三点加权 private ListLatLng smooth(ListLatLng raw) { ListLatLng out new ArrayList(); for (LatLng p : raw) { if (out.isEmpty()) { out.add(p); continue; } LatLng last out.get(out.size() - 1); if (DistanceUtil.getDistance(last, p) 5.0) continue; // 抽稀 out.add(p); } // 再做一次三点加权平均权重 0.25/0.5/0.25 // ... 见资源包 TrackSmoother.java return out; }参数说明5 米阈值适合步行和骑行车载可以放到 8 到 10 米。权重 0.25/0.5/0.25 是常用配置能压掉高频抖动又不明显滞后。3. 路径规划与导航状态机从算路到播报的完整链路3.1 算路请求的参数怎么填路径规划分驾车、步行、骑行、公交四类资源包里封装了RoutePlanManager。驾车算路要关注三个策略参数policy躲避拥堵、不走高速等、wayPoints途经点最多 16 个、avoidRoad避让路段。新手常犯的错是把途经点当终点传结果算出一条绕圈路线。// 驾车算路请求注意起终点坐标系必须与 SDK 一致 RoutePlanManager plan new RoutePlanManager(); plan.setFrom(new LatLng(startLat, startLon)); // 已转成 gcj02 plan.setTo(new LatLng(endLat, endLon)); plan.setPolicy(RoutePolicy.LEAST_TIME); // 时间优先 plan.setWayPoints(wayList); // 途经点可为空 plan.setAvoidRoad(拥堵路段ID); // 可选 plan.request(new RouteResultListener() { Override public void onSuccess(RouteResult result) { // result 里含距离、耗时、路段列表 } Override public void onFail(int code, String msg) { /* 见 3.3 错误码 */ } });参数说明policy在早晚高峰用LEAST_TIME比LEAST_DISTANCE更贴合实际wayPoints顺序会影响算路结果传之前先按行进方向排好。算路是网络请求要放在子线程回调再切主线程更新 UI。3.2 导航状态机五个状态别写乱导航不是“开始”和“结束”两个状态。资源包里定义了一个状态机IDLE空闲、PLANNING算路中、NAVIGATING导航中、REROUTING偏航重算、ARRIVED到达。每个状态能接收的事件是固定的比如NAVIGATING收到偏航事件才进REROUTING收到到达事件进ARRIVED。// 状态机核心事件驱动避免 if-else 堆叠 public void onEvent(NavEvent event) { switch (state) { case NAVIGATING: if (event NavEvent.DEVIATE) { state State.REROUTING; reroute(); // 触发重算 } else if (event NavEvent.ARRIVE) { state State.ARRIVED; stopNavi(); } break; case REROUTING: if (event NavEvent.ROUTE_OK) state State.NAVIGATING; break; // ... 其余状态 } }逻辑说明偏航判定靠“当前位置到规划路径的垂直距离”超过 50 米且持续 3 秒才触发重算否则等个红绿灯就重算流量和体验都崩。参数上50 米和 3 秒是经验值高架桥场景可以放宽到 80 米。3.3 导航播报与剩余信息计算播报分两类路口转向播报和电子眼播报。资源包里用TTSPlayer封装播报时机靠“剩余距离 当前车速”估算。剩余距离小于 300 米播一次小于 100 米再播一次避免连续播报打断音乐。// 播报触发逻辑 private void checkBroadcast(double remainDist, int speedKmh) { if (remainDist 300 !hasBroadcast300) { tts.speak(前方三百米 actionText); hasBroadcast300 true; } if (remainDist 100 !hasBroadcast100) { tts.speak(前方一百米 actionText); hasBroadcast100 true; } // 进入新路段时重置标志位 }参数说明speedKmh用于动态调整播报距离高速上 300 米不够要提前到 1000 米。资源包里按速度分了低速、中速、高速三档阈值直接可用。4. 后台保活与电量导航切后台就断的排查4.1 前台服务与通知栏Android 8.0 以后后台定位必须挂前台服务否则系统几分钟内就掐掉。资源包里用NavigationService继承Service在onStartCommand里调startForeground通知栏显示“导航进行中”。// 前台服务启动channel 必须提前创建 Override public int onStartCommand(Intent intent, int flags, int startId) { Notification notification buildNotification(); // 含导航状态 startForeground(NOTIFY_ID, notification); return START_STICKY; // 被杀后尝试重建 }参数说明START_STICKY适合导航被杀后系统会重建服务但 Intent 为 null要在onCreate里做状态恢复。通知栏的channel在 Android 8.0 以上必须建否则通知不显示前台服务也起不来。4.2 电量优化别让导航成电老虎导航是耗电大户资源包里做了三件事定位间隔动态调整停车时拉长到 5 秒、屏幕常亮只在导航页生效、TTS 播报合并。实测下来一小时导航耗电从 18% 降到 11% 左右。// 根据车速动态调整定位间隔 private void adjustScanSpan(int speedKmh) { if (speedKmh 5) { locationClient.setScanSpan(5000); // 停车或慢行 } else if (speedKmh 40) { locationClient.setScanSpan(2000); // 市区 } else { locationClient.setScanSpan(1000); // 高速 } }参数说明setScanSpan可以运行时改但频繁改会有抖动建议加 2 秒防抖。高速上 1 秒一次是必要的否则转弯提示会滞后。5. 避坑与排查导航功能最常见的五个翻车现场5.1 现象轨迹偏移几百米换地图 SDK 无效原因坐标系没对齐。GPS 原始坐标是 WGS-84地图渲染层要 GCJ-02中间少了一次转换。解决在定位回调里统一转成目标坐标系再入库展示层不再转。资源包CoordConverter直接调。5.2 现象偏航重算频繁触发流量暴涨原因偏航判定阈值太小或没做持续时间过滤。解决垂直距离阈值设 50 米持续 3 秒才触发同时把重算请求做 5 秒节流避免连续请求。5.3 现象切后台几分钟后导航停止原因没挂前台服务或通知 channel 没建导致前台服务启动失败。解决检查startForeground是否在onStartCommand里调用channel 是否在Application里提前创建。5.4 现象播报延迟路口过了才说话原因定位间隔太长或播报距离阈值没按速度调整。解决高速场景把scanSpan降到 1000ms播报距离按速度分档资源包里BroadcastThreshold可直接用。5.5 现象算路返回错误码但不知道哪错了原因起终点坐标系与 SDK 不一致或途经点超限。解决先确认setCoorType与算路接口要求一致途经点最多 16 个超了要分批。错误码对照表在资源包RouteErrorCode.java里。6. 进阶技巧用模拟轨迹做回归测试导航功能最难的是测试——你不可能每次都开车上路。资源包里带了一个MockTrackPlayer把一段真实轨迹存成 JSON按时间戳回放模拟定位回调。这样偏航、重算、播报逻辑都能在办公室验证。// 模拟轨迹回放用于回归测试 MockTrackPlayer player new MockTrackPlayer(); player.load(track_sample.json); // 含 lat/lon/timestamp player.setSpeedFactor(2.0); // 2 倍速回放 player.setCallback(location - { // 这里走和真实定位一样的处理链路 onLocationChanged(location); }); player.start();参数说明speedFactor控制回放速度测试偏航时调到 5 倍能快速触发track_sample.json里每条记录要有时间戳否则回放节奏不对。我一般会在发版前跑三组轨迹市区拥堵、高速、隧道丢失信号三组都过才敢提测。从那以后我每次接导航需求都强制先跑一遍坐标系转换和模拟轨迹回放再谈 UI 和播报。这两步省掉后面全是血泪。希望帮到你。本文还有配套的精品资源点击获取