资讯动态

Android集成高德地图SDK实现应用内导航功能全流程实战

发布时间:2026/9/7 8:19:28 来源:尧图企业网站定制
简介基于百度地图SDK的Android导航功能工程完整实现从定位、路线规划到导航引导的闭环。资源包内含1343个文件解压后约22.55MB主要文件类型包括XML布局与配置、PNG图片资源、JSON数据、Java源码、JAR依赖库及AIDL接口定义并附带调试版APK与构建产物可帮助开发者快速跑通项目并理解各模块间的调用关系。内容覆盖百度地图SDK集成、LocationClient定位、起终点搜索与POI设置、驾车/步行/骑行多方式路线规划以及导航过程中的语音提示、地图视角跟随、位置变化监听与路线重规划等关键细节。目前已有514人参与学习下载工程目录结构清晰注释完整适合有Android基础、希望深入掌握地图导航功能开发的工程师作为实战参考也能直接复用其中封装好的导航组件。1. 项目概述与整体思路1.1 导航功能到底要做什么提到“Android 实现导航功能”很多刚接触的人第一反应是“打开高德地图 App 让用户自己搜就行”。但真正的需求通常不是这样——电商 App 要展示配送轨迹社交 App 要分享实时位置工具类 App 要一键拉起目的地路线。这背后涉及的是定位能力、地图展示、路线规划、导航引导这四层能力每一层都有大量细节要处理。我接手这个项目时产品经理只丢过来一句话“给 App 加个导航点一下按钮就能去目的地。”这句话拆出来至少有 6 个子任务获取当前定位、展示地图、传入起点终点、算路步行/驾车/骑行、开始导航、导航过程中实时更新位置和语音提示。这也是我写这篇文章的初衷——从零到一完整跑通这套流程把每一步的关键代码和踩过的坑都记录下来给准备做类似功能的开发者一条可复制的路径。这里先声明一个方案边界本文方案是“在 App 内集成地图 SDK 完成导航”不是“调起系统地图 App”。两者的区别在于前者用户全程不用离开你的产品体验更完整但集成成本高后者接入成本极低体验割裂。我给大多数业务场景的建议都是集成 SDK原因后面细说。1.2 两条技术路线的取舍实现导航功能技术选型上基本就是两条路一条是接第三方地图 SDK高德、百度、腾讯另一条是自研地图引擎地图渲染 路网数据 路径规划算法。自研这条路我直接劝退——不是技术做不到而是成本完全不成比例。地图数据要持续采购和更新路径规划算法要处理全国路网的连通性、实时路况、禁行限制渲染引擎要适配各种机型和分辨率。即便团队有 20 个人投入半年做出来的效果也大概率不如成熟 SDK。反过来看第三方 SDK高德、百度都提供了从定位到导航的完整能力免费额度对中小型项目基本够用而且文档和社区积累都很丰富。它们在架构上把“地图渲染引擎”和“导航引擎”拆成了不同模块你可以只接地图不接导航也可以只接导航不展示地图按需依赖。这个设计对我这种只想要导航能力、不想要多余地图功能的开发者非常友好。在具体选型上我以高德地图为例展开因为它的开发者文档相对清晰、API 设计更贴近直觉而且比较好的一点是提供了“基础地图”和“导航 SDK”分开的依赖可以用最小体积实现需求。百度和腾讯的方案思路大同小异代码换皮即可核心概念是一样的。2. 环境准备与地图 SDK 接入2.1 申请 Key 的全流程接入高德 SDK 第一步是去高德开放平台创建应用获取 Key。这个 Key 是全局校验凭证必须在 AndroidManifest.xml 里以meta-data的方式声明。申请 Key 的时候需要填两项应用包名和 SHA1 签名指纹。这里有个常见误区——很多人第一次用debug.keystore的 SHA1 申请 Key结果打 Release 包之后地图白屏因为 Release 签名和 Debug 签名不一样高德会校验 Key、包名、签名三者是否匹配。获取 SHA1 的方法我不展开讲太多核心就是两个命令用 Android Studio 右侧 Gradle 面板找到signingReport任务跑一下或者用命令行keytool -list -v -keystore ~/.android/debug.keystore默认密码 android。建议两个签名都申请一个 Debug 一个 Release避免打包炸掉。另外强调一点Key 一定要绑定包名不能图省事用一个 Key 跑多个项目否则后面排查问题会非常痛苦。申请完 Key在 AndroidManifest.xml 里加这样一段application meta-data android:namecom.amap.api.v2.apikey android:value你的Key / /application2.2 Gradle 依赖与 so 库配置高德 SDK 的依赖现在可以通过 Maven 远程拉取省去了手动下载 jar 包的麻烦。在build.gradle的dependencies块里加implementation com.amap.api:3dmap:latest.release // 3D 地图 implementation com.amap.api:location:latest.release // 定位 implementation com.amap.api:search:latest.release // 搜索POI 搜索 implementation com.amap.api:navi:latest.release // 导航这里有一个新手容易踩的坑navi依赖会带上地图引擎所以如果你同时依赖了3dmap和navi会出现两个地图引擎包。如果你只需要导航能力建议只依赖navi和location不要重复依赖3dmap否则 APK 体积会变大而且可能出现类冲突。我自己第一次接入时就是四个依赖全加打出的包硬生生多了 30MB。同时需要在android块里配置sourceSets指定 so 库架构android { sourceSets { main { jniLibs.srcDir libs } } }不过从高德新版开始so 库会从 Maven 里自动带过来这段配置是给老项目从本地 libs 目录迁移时用的。保险起见建议在defaultConfig里加一句只保留armeabi-v7a和arm64-v8a因为高德的 so 库没有x86架构模拟器上默认跑就会加载失败这也是模拟器上地图白屏的经典原因之一。2.3 权限声明与 Android 版本适配导航功能绕不开定位权限。Android 6.0 以上需要动态申请Android 10 以上如果要在后台持续定位还需要ACCESS_BACKGROUND_LOCATION权限Android 12 开始新增了精确位置和模糊位置的区分。这里直接给出一份最小可用的权限清单uses-permission android:nameandroid.permission.INTERNET / uses-permission android:nameandroid.permission.ACCESS_NETWORK_STATE / uses-permission android:nameandroid.permission.ACCESS_WIFI_STATE / uses-permission android:nameandroid.permission.ACCESS_COARSE_LOCATION / uses-permission android:nameandroid.permission.ACCESS_FINE_LOCATION / uses-permission android:nameandroid.permission.WRITE_EXTERNAL_STORAGE / uses-permission android:nameandroid.permission.READ_PHONE_STATE / uses-permission android:nameandroid.permission.FOREGROUND_SERVICE /注意READ_PHONE_STATE在 Android 6.0 之后也属于危险权限但高德定位 SDK 在某些版本会依赖它获取设备标识辅助定位。如果你的 App 不申请这个权限也能定位只是精度下降一点。要不要申请取决于你的隐私合规策略实际项目中我自己倾向于不申请避免引发用户对隐私的担忧。还有一个不少人忽略的uses-feature声明。有些应用商店在审核的时候会检查 APK 里是否声明了android.hardware.location.gps如果你的设备列表不支持 GPS会直接导致下载后导航功能不可用。建议在 Manifest 里加上uses-feature android:nameandroid.hardware.location.gps android:requiredfalse /这样写的意思是“有 GPS 就用没有也能跑”避免因为功能声明太硬把不支持 GPS 的设备直接排除掉。3. 地图与定位能力搭建3.1 地图控件的生命周期管理地图控件是导航功能的地基地基不稳后面全白搭。高德地图主控件是MapView或TextureMapView两者区别在于前者基于SurfaceView后者基于TextureView支持在动画、滚动等场景中正常展示而不会出现黑屏。如果你的地图会嵌在 ScrollView、ViewPager 或者有转场动画的页面里建议优先用TextureMapView。地图控件有一个十分关键的硬性要求必须把生命周期回调传给地图引擎。在 Activity 或 Fragment 里要手动调用Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mapView.onCreate(savedInstanceState); } Override protected void onResume() { super.onResume(); mapView.onResume(); } Override protected void onPause() { super.onPause(); mapView.onPause(); } Override protected void onDestroy() { super.onDestroy(); mapView.onDestroy(); }漏掉任何一个回调轻则地图不显示重则内存泄漏、GL 线程崩溃。很多人在退出页面 5 分钟后收到OpenGLRenderer相关的崩溃日志多半就是onDestroy没调用。这里我建议封装一个BaseMapActivity把生命周期绑定逻辑写死在基类里想犯错都难。3.2 定位能力与权限动态申请地图展示只是底图导航的灵魂是“知道你在哪、要到哪去”。高德的定位 SDK 提供 AMapLocationClient它是单例模式初始化的时候传入 Context 和定位回调。核心代码长这样AMapLocationClient locationClient new AMapLocationClient(context); AMapLocationClientOption option new AMapLocationClientOption(); option.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy); option.setInterval(2000); locationClient.setLocationOption(option); locationClient.setLocationListener(new AMapLocationListener() { Override public void onLocationChanged(AMapLocation location) { if (location.getErrorCode() 0) { double lat location.getLatitude(); double lng location.getLongitude(); // 更新地图中心点 } else { // 定位失败根据 errorCode 判断原因 } } }); locationClient.startLocation();Hight_Accuracy模式会同时开启 GPS 和网络定位优先返回精度高的结果。这里要想清楚什么时候用setInterval(2000)持续定位什么时候用setOnceLocation(true)拿一次结果——导航过程中需要持续回调但你的业务如果只是“打开页面定位到当前城市”用一次定位就够了持续定位非常耗电还会被系统在后台限制频率。定位权限的动态申请处理建议用ActivityCompat.requestPermissions这个工具一次性申请ACCESS_FINE_LOCATION和ACCESS_COARSE_LOCATION回调里判断 SDK 版本和用户是否开启定位服务。有一个项目经验是如果你的 App 有“引导去开启定位服务”的流程会比直接弹系统定位开关更友好因为很多用户根本不知道系统定位服务在哪里开。3.3 从定位点展示到点击选点地图能定位了下一步是让用户选一个目的地。这个交互有两种实现方式第一种是地图上长按选点第二种是通过 POI 搜索输入关键词选点。长按选点相对简单只要监听AMap.OnMapLongClickListener在回调里拿到经纬度并添加 Marker 标记。POI 搜索则是调用搜索 SDK 的InputtipsQuery和GeocodeSearch把用户输入的关键词转成坐标。两种方式可以结合用我项目的交互是用户在地图上长按选点同时底部弹出一个搜索框搜索出来的结果点可以直接替换这个点。添加 Marker 的代码也不复杂aMap.addMarker(new MarkerOptions() .position(new LatLng(lat, lng)) .title(目的地) .snippet(点击开始导航) .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_RED)));但有个地方需要注意如果用户反复选点旧的 Marker 要清掉否则屏幕上会堆满红点。处理方案是在选新点之前调用aMap.clear()或者维护一个Marker引用选新点时marker.remove()这两种方式都可以但个人更推荐用clear()——它同时会清掉路线规划图层避免路线残留。4. 路径规划与导航流程实现4.1 算路的几种方式与策略选择用户选好起点和终点之后核心操作就是“算路”即向高德服务器发起路径规划请求拿到一条可行驶的路线。高德提供了多个算路口calculateDriveRoute驾车、calculateWalkRoute步行、calculateRideRoute骑行、calculateBusRoute公交。常规做法是根据业务给用户一个出行方式的切换入口默认选驾车。驾车算路的策略参数挺多AMapNaviSetting里有 AVOID_CONGESTION避让拥堵、AVOID_HIGHWAY不走高速、SAVE_MONEY避免收费等枚举可以组合使用。这里有一个使用心得不要一开始就把策略设得太复杂calculateDriveRoute(ListLatLng wayPoints, ListNaviPoi endPoints, int strategy, boolean isRefresh)这个方法中如果传入多个途经点算路时间会明显变长而且免费额度的并发请求也可能被限流。你的业务如果只是单点导航传一个终点就够了。算路是异步的结果通过回调方法返回navi.calculateWalkRoute(startPoint, endPoint, new AMapNaviListener() { Override public void onCalculateRouteSuccess(int routeId) { // 规划成功拿到路线 } Override public void onCalculateRouteFailure(int errorInfo) { // 规划失败errorInfo 是错误码 } });算路成功不等于导航成功。网络差、起点/终点在海里、路线跨区域管控等场景都会导致算路失败。建议做一层兜底 UI失败时弹 Toast 或 Snackbar不要让用户感觉按钮点了没反应。另外算路之前先检查网络高德的错误码 4 就是网络异常如果 App 已检测到无网络可以直接提示避免等超时。4.2 启动导航仪与导航组件算路成功之后拿着拿到routeId就可以启动导航界面了。高德有两种导航界面方案一种是使用高德提供的NavigationView组件自动包含地图渲染、转向箭头、语音播报、距离提示等全套 UI开发成本很低另一种是自绘 UI只用导航引擎的核心数据经纬度、方向角、剩余距离自己画箭头和文字适合需要高度定制 UI 的产品。NavigationView是一种 Fragment先传入算路成功的routeId再调用startNavi(int naviMode)传AMapNavi.EmulatorNaviMode是模拟导航不真实读取 GPS按算出来的路线模拟走一遍传AMapNavi.GPSNaviMode是 GPS 实时导航。实测直接传 GPS 模式即可模拟导航主要用于调试和录演示视频别把它当成正式功能——如果用户在真实道路上打开模拟导航路线画面会严重错位。我自己写导航页时是直接把NavigationView放进一个单独 Activity 里的if (navi.startNavi(AMapNavi.GPSNaviMode)) { // 成功进入导航组件 }注意startNavi返回布尔值如果返回 false说明导航引擎没有处于“已算路成功”的状态要检查是否在算路之前调用了navi.destroy()或者多次 init 了同一个导航实例。4.3 导航过程的关键回调进入导航组件之后地图上会实时更新车辆位置自动转向语音播报。但这个过程中产品经理大概率会提一个需求“导航要能主动弹一个小卡片显示剩余的公里数和预计到达时间。”这个数据从哪来答案是导航监听回调。在使用NavigationView的同时可以注册一个AMapNaviListener来监听导航状态。常用的回调有这么几个Override public void onNaviInfoUpdate(NaviInfo naviInfo) { // 每次更新获取当前路况、剩余距离、剩余时间 int curStepRemainDist naviInfo.getCurStepRemainDist(); int remainDist naviInfo.getPathRemainDist(); int remainTime naviInfo.getPathRemainTime(); } Override public void onArrivedDestination(boolean isArrived) { // 到达终点 } Override public void onGpsSignalWeak() { // 当前 GPS 信号弱比如进了隧道 }这些回调在主线程触发可以直接更新 UI。但要注意回调频率非常高onNaviInfoUpdate在车辆转弯、变道、里程变化时都会调用如果你在里面做复杂运算或数据库写入会卡顿甚至 ANR。建议只做 UI 更新数据变化量多的时候用 Handler 做节流。4.4 路线绘制与自绘导航的补充方案如果你决定不用NavigationView而是自绘导航 UI路线图层的绘制就绕不开。高德地图提供了AMap.Navi内部类用于绘制路线在算路成功回调中拿到NaviPath后可以用NaviPath naviPath navi.getNaviPath(); ListLatLng pathLatLngs naviPath.getCoordList(); aMap.addPolyline(new PolylineOptions() .addAll(pathLatLngs) .width(20) .color(Color.BLUE) .setCustomTexture(elevatedRoadTexture));自绘的好处是路线颜色、粗细、箭头样式都能按 UI 规范定制不用接受高德默认的蓝色路线。坏处是要自己维护导航指示箭头、道路名称卡片、测速提示等等工作量比想象中大得多。我建议第一个版本老老实实用NavigationView等业务跑通、用户反馈稳定了再考虑自绘。这个观点可能有些保守但是避免发布延期的最稳妥路径。5. 常见问题与避坑实录5.1 高频问题速查表下表是根据我实际项目和社群反馈整理的高频问题基本覆盖了大部分“导航功能怎么不工作”的案例现象大概率原因解决建议地图白屏或网格签名/包名和 Key 不匹配重新核对 SHA1、包名用aMap日志查看错误码定位权限弹窗后闪退未动态申请权限或权限声明缺失检查 Manifest 权限按 Android 版本走动态流程算路一直失败errorCode 4网络异常或高德服务器限流检查网络稍后重试先判断网络状态模拟器上地图崩溃x86 架构没有对应的 so 库用真机调试或给模拟器配置 ARM 镜像导航界面是黑屏TextureMapView未绑定生命周期检查onResume/onPause/onDestroy回调Release 包 Key 失效Release 签名未在控制台配置申请时同时添加 Debug 和 Release 的 SHA15.2 两个容易忽视的实操细节第一个细节是“导航前检查 GPS 开关”。很多用户手机的 GPS 总开关是关闭的系统定位使用基站和 WiFi 也能粗定位但导航实际需要连续的 GPS 信号。如果 GPS 总开关没开算路成功后面会不断回调onGpsSignalWeak用户体验极差。实现上可以在进入导航页前用LocationManager.isProviderEnabled(GPS_PROVIDER)检查如果没开弹窗引导打开这是低成本但能显著减少客诉的一个检查项。第二个细节是“onDestroy 里释放导航引擎”。导航 SDK 比较吃资源正常退出和异常退出都要调用navi.destroy()和locationClient.onDestroy()否则几十次进出之后App 会明显变卡甚至触发系统杀进程。这个看起来是小事但实际线上 crash 占比不低。5.3 关于导航 SDK 版本与功能扩展的思考高德 SDK 更新比较频繁不同大版本的 API 会有细微变动。我看到有些老项目还在用 2.x 的版本但新项目建议直接用 3.x 系列因为文档、示例代码、社区问答大多围绕新版本。跨大版本升级时要注意 Gradle 依赖的传递性——如果同时依赖了高德多个包它们的版本号建议统一否则可能出现AMapLocationClient类和AMapNavi类来自不同版本导致运行时异常。最后一个建议是功能扩展的角度。导航跑通之后大概率会接到“轨迹回放”“电子围栏”这类周边需求。高德的AMapNavi和AMapLocationClient其实都预留了相关数据接口比如轨迹回放可以直接用onNaviInfoUpdate中的坐标序列电子围栏可以用定位回调里的经纬度做Polygon判交。所以做第一步集成的时候建议把导航相关的数据算路成功后的坐标点、定位点都先缓存一份到内存或数据库为后续功能做准备避免到时候要回补埋点。走到这一步一个可以真正投入使用的 App 内导航功能已经完整落地了。回过头看集成的核心难点其实不在 API 调用——文档里都写得明明白白——反而在于对 Android 系统权限、生命周期、多版本适配的把握以及对自己业务交互的梳理。导航功能看似是一个独立模块但它的稳定性和流畅度依赖整个 App 的底层工程规范。希望这些在项目里实际撞过的坑能帮你少走一段弯路。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价