Android导航Demo:定位、路线规划与模拟导航集成实战
简介一份面向Android开发者的百度地图导航功能工程资源覆盖从开发包集成、权限声明、位置获取、起终点设置、路线规划到导航引导的完整链路适合需要快速为已有应用接入地图导航模块的中级开发者。压缩包为rar格式共1343个文件约22.55MB内含大量布局与配置文件、图标素材、Java源码、依赖库、AIDL接口、JSON数据、Gradle构建脚本以及可直接运行的调试安装包基本覆盖一个Android项目从源码资源到编译产物的各个层面目录结构清晰。已有520人学习下载。通过该工程可对照理解百度地图位置监听、路线检索、路径绘制、语音播报等核心API的实际用法同时可基于现成的界面与构建配置直接运行调试并便捷地扩展驾车、步行、骑行等出行方式对于排查SDK接入、定位失败、路线不显示等常见问题也有直接参考价值是地图导航模块一个完整的起步模板。1. 导航功能不是从零写的这份Demo把集成路线走通了拿到这份工程之前我也有一个执念App 里做导航要么是调起外部地图应用要么是把路线规划算法搬进项目里自己算。把工程跑起来之后才发现常规做法远没有这么重——地图展示、实时定位、路线规划、导航状态推进每一层都拆成独立模块再通过事件回调串成一条链路。这份 Demo 适合两类人一类是业务系统里需要“路线预览”和“模拟导航”能力的 Android 开发另一类是做物流配送、外勤打卡类项目想快速把定位和导航能力嵌进自己 App 的团队。下面我按工程结构、核心代码、落地细节和避坑记录逐个拆开讲。2. 工程骨架拆解从一个按钮到一条可导航路线的完整链路2.1 目录结构每个包只负责导航链路里的一环工程不是那种“所有代码堆在一个 Activity 里”的老式写法打开根目录就能看出模块边界NavDemo/ ├── app/ │ ├── build.gradle │ └── src/main/ │ ├── AndroidManifest.xml │ ├── java/com/demo/nav/ │ │ ├── MainActivity.kt │ │ ├── map/MapViewHolder.kt │ │ ├── location/LocationHelper.kt │ │ ├── route/RoutePlanService.kt │ │ ├── nav/NavigationManager.kt │ │ └── widget/RouteInfoPanel.kt │ └── res/ └── gradle/逐层说职责。MainActivity只做两件事初始化地图视图把四个子模块组装起来MapViewHolder负责地图生命周期包括创建、销毁和相机控制LocationHelper封装定位 SDK 的启动停止和权限申请对外只暴露一个“位置更新”回调RoutePlanService接收起终点坐标发起路线规划请求并解析结果NavigationManager是整个链路的调度者持有状态机把路线数据交给地图绘制同时接收导航事件回调RouteInfoPanel是底部信息面板展示剩余距离、剩余时间。这样一个包里只有一个文件看着简单但对于接入到自己项目里反而更方便——想删掉语音播报就只动NavigationManager想换定位策略就只改LocationHelper不需要在一千行的 Activity 里找逻辑。2.2 技术选型地图 SDK、定位、路线规划三者怎么协作先说清楚一个常见的误解路线规划不是地图 SDK 的隐藏功能而是独立的服务只是地图 SDK 通常会附带这个能力。这里的 NavMap SDK 泛指国内常见的移动端地图 SDK接口细节可能略有差异但套路一致。工程里三者协作的时序是这样的MainActivity创建地图后先启动定位拿到当前经纬度作为路线起点用户输入终点后RoutePlanService带着起终点坐标去请求路线返回的路线数据被拆成两个部分——一条用于地图绘制的Polyline点串一组用于导航状态机的“转向指令”NavigationManager进入导航中状态后开始监听定位回调把真实位置投射到路线附近同时每走到一个拐弯点就触发一次语音播报和面板更新。用户点击“开始导航” ↓ RoutePlanService.requestRoute(origin, destination) ↓ 返回 DriveRouteResult ↓ NavigationManager.startNavigation(route) ↓ 订阅定位回调 启动模拟导航 ↓ 按路段索引推进播报 → 更新时间 → 判断到达这段链路里最容易忽略的是“定位来源”和“路线坐标”不是同一套坐标系大部分地图 SDK 内部已经做了转换但如果你自己对接第三方定位 SDK就一定要确认坐标系是 GCJ-02 还是 WGS-84否则画出来的路线会整体偏移一百米左右。2.3 生命周期管理导航对象跟着页面走而不是跟着进程走地图是重资源SDK 都要求在 Activity 的onResume、onPause、onDestroy里调用对应生命周期方法。工程里MapViewHolder做了这件事class MapViewHolder(private val mapView: MapView) { fun onResume() { mapView.onResume() locationHelper.start() } fun onPause() { mapView.onPause() locationHelper.stop() } fun onDestroy() { mapView.onDestroy() NavigationManager.instance.release() } }说明定位如果不在onPause停掉退出页面后定位回调还会持续上报造成耗电和内存泄漏。这里把LocationHelper.start/stop和地图生命周期绑在一起可以确保页面不可见时定位链路整体挂起。NavigationManager是单例但加了一个release()方法清理回调监听否则跨页面持有 Activity 里的视图引用是老项目最常见的崩溃来源之一。3. 核心链路代码复盘定位、路线规划与导航状态机3.1 定位封装从权限申请到坐标回调LocationHelper是个典型封装调用方不关心 SDK 细节只接一个回调class LocationHelper(private val context: Context) { private var listener: ((LatLng) - Unit)? null fun setOnLocationChanged(listener: (LatLng) - Unit) { this.listener listener } fun start() { if (!hasPermission()) { requestPermission() return } // 间隔1秒回调一次移动超过10米也触发一次 NavMapSDK.startLocation( interval 1000L, distanceFilter 10f, callback { latLng - listener?.invoke(latLng) } ) } fun stop() { NavMapSDK.stopLocation() } private fun hasPermission(): Boolean { return ContextCompat.checkSelfPermission( context, Manifest.permission.ACCESS_FINE_LOCATION ) PackageManager.PERMISSION_GRANTED } }参数说明interval是定位回调的时间间隔单位毫秒导航场景下 1000 毫秒是比较合适的折中太密耗电太疏会导致转弯播报滞后distanceFilter表示移动多少米才触发下一次回调设成 10f 能过滤掉定位漂移带来的抖动。这里有个细节——没有权限时我没有立刻弹申请而是先返回权限结果通过onRequestPermissionsResult再调一次start()。很多新手在这里踩坑权限回调里直接判断granted就继续走忘记重新触发定位启动。3.2 路线规划把起终点坐标变成一条可行驶的路线路线规划核心代码在RoutePlanServiceclass RoutePlanService { fun requestRoute(origin: LatLng, destination: LatLng, callback: (DriveRoute?) - Unit) { NavMapSDK.routeSearch().driverRoute( origin origin, destination destination, wayPoints emptyList(), strategy RouteStrategy.SHORTEST_TIME ) { result, error - if (error ! null) { callback(null) returndriverRoute } // 取第一条推荐路线 val route result?.routes?.firstOrNull() callback(route) } } }说明这里把“选路策略”直接写死了是SHORTEST_TIME实际项目中我一般会把策略做成一个下拉选项让用户选“最快”“最短”“避开高速”。注意wayPoints参数传空列表表示普通起终点导航如果用在外勤系统里这里可以塞途经点列表实现多点配送路径。路线规划返回的DriveRoute不是一个孤立的点串它里面带着latitudeList路线形状点、stepList转向指令列表、distance总长度、duration预计耗时。绘制到地图上只取latitudeList但导航播报必须遍历stepList两者要区别使用拿到对象后别一股脑扔给MapAdapter。3.3 导航状态机空闲、规划中、导航中、到达导航最怕“状态乱”。用户反复点击开始、退出、重新规划时如果不用状态机约束会出现语音重复播报、路线重叠绘制、定位还在跑但页面已经是结果页等怪现象。工程里的NavigationManager用枚举管理状态enum class NavState { IDLE, // 无任务 ROUTING, // 正在请求路线 NAVIGATING, // 导航中 ARRIVED // 已到达 }class NavigationManager private constructor() { private var state NavState.IDLE fun startNavigation(route: DriveRoute) { if (state NavState.NAVIGATING) { stopNavigation() // 先回收旧任务 } state NavState.NAVIGATING routeIndex 0 // 开始模拟导航每秒推进一步 simulateNavigation(route) } fun stopNavigation() { state NavState.IDLE NavMapSDK.stopSimulate() listener?.onNavigationStopped() } }状态机的意义在于任何外部操作都先判断当前状态比如“开始导航”按钮在ROUTING和NAVIGATING状态下可以直接复用不需要针对每个入口加布尔开关。stopNavigation()里我加了一个先导动作确保重复点击时旧任务一定被清理不会出现两个定时器同时在推前进位置。4. 导航体验落地模拟导航、语音播报与实时纠偏4.1 模拟导航没有真机也能演示完整跑线流程开发导航功能最头疼的是测试——总不能在真机上真的开全程车。工程里做了一个很实用的设计模拟导航模式给定一个速度让“假位置”沿路线匀速前进。private fun simulateNavigation(route: DriveRoute) { val points route.latitudeList NavMapSDK.startSimulate( points points, speed 60f, // 模拟行驶速度 km/h onLocationTick { position - // position 是当前在路线上的插值坐标 updateCamera(position) checkAndHandleTurn(position) updateRoutePanel(position) }, onArrived { state NavState.ARRIVED listener?.onArrived() } ) }说明startSimulate是地图 SDK 提供的能力传入整条路线点串SDK 会按照speed参数在点与点之间插值自动产生平滑的位置上报。这里速度我给的是 60km/h适合演示如果你给调度车辆做轨迹预览可以把速度调成 20km/h 看路口细节。onLocationTick回调里的checkAndHandleTurn(position)是播报的关键逻辑每次回调都判断当前位置距离下一个转向点还有多远小于阈值就触发播报并切换索引。4.2 到达判断与纠偏不要只比“距离终点小于N米”到达判断是导航功能里最容易被做假的点。简单实现往往是“距离终点小于 50 米就算到达”但实际路网里终点的 GPS 信号可能漂移或者路线终点和车实际能停的位置有偏差导致导航还没结束就弹出到达页。工程里的到达逻辑参考了 SDK 自带的状态回调自己只做兜底private fun checkArrive(position: LatLng, route: DriveRoute) { val remainDistance route.stepList.sumOf { step - // 当前路段剩余 后续路段总长 step.remainDistance(position) } // 同时满足“剩余距离近似0”和“接近路线终点”两个条件 if (remainDistance 30f position.distanceTo(route.destination) 60f) { state NavState.ARRIVED listener?.onArrived() } }逻辑说明两个条件必须同时成立避免单个条件误判。比如路线终点恰好在一片 GPS 信号遮挡区位置漂移让“距终点”小于 60 米但剩余距离还很长此时不会误报到达。反过来如果剩余距离已经很小但真实位置离终点还有 100 米因为路线结束点在辅路也不会提前结束。这套双条件写法在真实路测里比单一阈值稳定得多。4.3 语音播报把转向指令转成可听懂的路口提示语音播报在工程里没有接大而全的 TTS 服务商而是通过NavigationManager内部维护一个指令队列到点后取出一条文案交给系统 TTSprivate fun checkAndHandleTurn(position: LatLng) { val currentStep steps[routeIndex] if (position.distanceTo(currentStep.turnPoint()) 80f) { // 到播报点朗读指令 tts.speak( 前方${currentStep.action}${currentStep.roadName}, finish { routeIndex listener?.onStepPassed(routeIndex) } ) } }参数说明turnPoint()是转向指令对应的导航点80f是提前播报的半径车速越快这个值要适当放大否则语音还没念完路口已经过去了。路口播报之后立刻滚动routeIndex保证下一次播报用的是下一个转向点。这里有个很容易漏的坑如果用户中途偏航routeIndex必须重置否则播报永远停留在旧路线上——下一章的避坑手册里我把这条列在了第一位。5. 避坑手册权限、生命周期与偏航恢复的五个常见问题5.1 高频故障现象、原因、解决现象原因解决地图正常路线画在地图之外定位坐标与地图 SDK 坐标系不一致常见于先接了一个 WGS-84 定位源再叠加地图确认坐标系统一用地图 SDK 自带的坐标转换接口或换用 SDK 内置定位点击开始导航后没有任何反应路线请求异步返回为空因为终点坐标非法或超出服务区在RoutePlanService回调里先判断route null弹 toast 并回退状态为IDLE不要自动跳到导航退出页面后语音还在播报NavigationManager是单例生命周期没有随页面销毁在页面onDestroy里调用stopNavigation()并注销 TTS 监听模拟导航一到路口就“飞线”定位漂移把位置拉出路线更新相机时直接跟随漂移点对onLocationTick的结果做路线吸附取路线投影点作为展示坐标第二次进入页面出现两个地图Activity 重建时MapViewHolder没有在onCreate里做状态恢复在MapViewHolder保存经纬度与缩放级别重建后调用mapView.onRestoreInstanceState恢复5.2 排查技巧用最小链路定位导航问题导航功能一旦出了问题最忌讳的是直接在真机上开着车看日志。我在跑这个工程时有一个固定流程先确认“定位有没有上报”再确认“路线有没有画出来”最后才看导航状态推进。如果定位回调一直为空优先查权限和 SDK Key如果路线能画但不能开始导航查NavigationManager.state是否停在ROUTING如果模拟导航不动查startSimulate的points是否被传成空列表。一个更省事的做法是在NavigationManager里加一个 debug 日志开关把每次状态切换打印出来private fun logState(from: NavState, to: NavState) { if (BuildConfig.DEBUG) { Log.d(NavTag, state: $from - $to) } }做法说明状态机的好处在这里体现得特别明显——只要状态切换是有序的链路就是健康的如果发现日志里出现NAVIGATING - NAVIGATING这样自己切自己的情况说明有外部调用没有经过stopNavigation()直接定位到那个入口即可不用把整个页面逻辑翻一遍。6. 二次开发把这套导航工程改成自己业务里的轮子拿到工程之后最常见的一个改造诉求是“把路线预览放在下单详情里”。做法是把第 3 章里规划的链路从MainActivity中抽出来改成一组 fagment先请求路线画一条不可交互的Polyline再根据用户点击进入完整导航。这里有一个轻量改法只动NavigationManager把它暴露的监听器扩展成业务需要的回调集合interface NavEventObserver { fun onStepPassed(index: Int) {} fun onArrive() {} fun onProgress(distance: Float, duration: Long) {} }NavigationManager内部维护一个Observer列表业务方按需注册。比如配送 App 只需要onProgress来实时回传位置不需要语音播报那就只实现这一个方法。这样做比在单例里写业务回调更干净也方便单元测试——测试时可以注入一个假的NavEventObserver直接验证状态机每一步推演是否正确。改造前先想清楚两件事第一路线规划是网络请求回调线程可能不在主线程所有 UI 更新要自己post回主线程第二业务系统里如果已经有自己的账号体系和本地缓存不要把定位数据和路线数据也一股脑放进去建议单独建一个NavRepository只负责导航链路的数据存取这样将来换地图 SDK 时只改仓储实现其他层完全不动。我从这个 Demo 里学到的最大一条教训是导航功能不要从“能不能走通”开始验证要从“状态切换是否闭环”开始验证。从那以后每次接手类似项目我都先画一遍状态图再跑一遍最小可运行链路确认IDLE - ROUTING - NAVIGATING - ARRIVED每一步都有日志可查才敢往上叠业务逻辑。这套笨办法帮我少踩了好多坑希望帮到你。本文还有配套的精品资源点击获取

相关新闻

Java底层面试实战包:JVM/AQS/Spring源码级调试验证

Java底层面试实战包:JVM/AQS/Spring源码级调试验证

简介:这是一份专为Java中初级至中高级研发人员打造的2026大厂面试系统性备战指南,直击校招与社招中“死记硬背失效、源码追问卡壳、新特性无从应对”三大核心痛点。资源以PDF形式交付,共1个文件,大小1.45MB,内容精炼紧…

2026/10/18 14:31:35 阅读更多 →
智诺方AI|论文引言与研究背景,降重同时弱化AIGC痕迹

智诺方AI|论文引言与研究背景,降重同时弱化AIGC痕迹

「研究背景与引言」别只顾赶进度,检测风险要提前管 研究背景与引言最容易写成「宏大叙事套话」。AI 生成引言往往从行业背景层层下铺,缺少与本选题绑定的问题意识,查重与 AIGC 双双承压。 与其临交稿前整章重写,不如按智诺方AI支持…

2026/10/18 14:31:35 阅读更多 →
为什么LFM2.5-24B-A2B-Preview总是先思考再回答:think标签推理机制与ChatML模板深度解析

为什么LFM2.5-24B-A2B-Preview总是先思考再回答:think标签推理机制与ChatML模板深度解析

【免费下载链接】LFM2.5-24B-A2B-Preview-GGUF 项目地址: https://ai.gitcode.com/hf_mirrors/unsloth/LFM2.5-24B-A2B-Preview-GGUF 点击查看 免费下载 LFM2.5-24B-A2B-Preview 是 Liquid AI 推出的 24B 总参数、2B 激活的纯推理大模型:它每次回答前都…

2026/10/18 14:31:35 阅读更多 →

最新新闻

MATLAB脑电信号预处理实战:避开基线漂移与眼电伪迹的5大深坑

MATLAB脑电信号预处理实战:避开基线漂移与眼电伪迹的5大深坑

简介:本资源是一套基于MATLAB实现脑电信号情绪分类的完整分析方案,面向生物医学工程、神经科学及人工智能交叉领域的初学者与进阶学习者,聚焦EEG信号预处理、特征提取与多情绪状态(愤怒、平静、悲伤)分类建模。压缩包共…

2026/10/18 15:24:02 阅读更多 →
不同平台投流成本大对比:底层逻辑、实测数据与避坑指南

不同平台投流成本大对比:底层逻辑、实测数据与避坑指南

1. 先别急着抄作业:投流成本这件事,水比你想的深做投放这行久了,被问得最多的问题就是"哪个平台便宜?"——但这个问题本身就有问题。不同平台投流成本大对比,单看后台显示的CPM、CPC、OCPM这些数字&#xff…

2026/10/18 15:24:02 阅读更多 →
3D医学影像分类实战:不跑3D CNN,用特征工程+LightGBM也能出结果

3D医学影像分类实战:不跑3D CNN,用特征工程+LightGBM也能出结果

简介:这是一份面向机器学习课程大作业、期末项目与毕业设计的3D医学影像分类实现,适合计算机、电子信息、数学等专业学生快速上手。资源包含完整的Python源代码与说明文档,覆盖数据预处理、模型训练、评估与提交等环节,能够帮助读…

2026/10/18 15:24:02 阅读更多 →
专业化健康管理实训室建设指南:从设备选型到课程融入

专业化健康管理实训室建设指南:从设备选型到课程融入

专业化健康管理实训室,助力康养人才精准培育这几年我陆续参与了几个康养类职业院校的实训室建设项目,期间走了一圈下来最大的感受就是:健康管理实训室和传统的护理实训室完全是两码事。很多学校在建设初期把重点放在了“设备多不多、房间够不…

2026/10/18 15:24:02 阅读更多 →
2026年Cursor平替工具推荐:TaoToken统一Key接入Trae与VS Code的零迁移方案

2026年Cursor平替工具推荐:TaoToken统一Key接入Trae与VS Code的零迁移方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 15:24:02 阅读更多 →
高并发场景下HTTP服务框架怎么选?压测数据与并发模型对比实践

高并发场景下HTTP服务框架怎么选?压测数据与并发模型对比实践

多年以前我在某技术团队做高并发系统重构,当时面临一个绕不开的问题:HTTP服务框架到底选哪个。团队里争论很久,最后大家决定不看文档吹嘘,不看社区热度,直接用压测数据说话。那段时间我们把线程模型框架、异步事件模型…

2026/10/18 15:23:02 阅读更多 →

日新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 0:55:02 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →