首页 / 资讯中心 / 文章详情

Android跑步App实战:高德地图SDK集成与轨迹绘制

Android跑步App实战:高德地图SDK集成与轨迹绘制 ★ FEATURED ARTICLE
简介这是一份基于 Android Studio 与 Java 开发的运动跑步类 App 完整源码面向具备一定安卓基础、希望学习定位与运动数据可视化的开发者可用于课程设计、毕业设计或二次开发。项目围绕跑步场景实现实时速度记录、跑步路径绘制、历史数据管理与详情查看等核心功能并集成地图 SDK 完成轨迹展示。压缩包共 129 个文件约 18.03MB包含 22 个 java 源码文件、32 个 xml 布局与配置、56 个 png 图片资源以及 so 库、jar 包和 gradle 构建脚本结构完整、可直接导入 Android Studio 运行调试。目前已有 2540 人学习下载适合作为安卓定位与运动类应用的实战参考帮助读者理解地图轨迹绘制、数据持久化与界面组织方式快速搭建自己的跑步记录工具。1. 从一份只有 9 个文件的 Android 跑步 App 说起一个能跑起来的安卓跑步 App核心文件可能比你想象的少得多。这份资源里只有gradlew.bat、两个.gitignore、两个build.gradle、settings.gradle、gradlew、一个高德地图 SDK 的 jar 包、一个gradle-wrapper.jar以及一个mapActivity.java。没有花哨的架构没有 MVVM没有 Jetpack Compose就是一个 Activity 扛起实时速度、路径绘制、履历管理和数据详情四件事。适合谁适合刚学完 Android 基础、想找一个能跑通「定位 地图 数据持久化」闭环的练手项目的人也适合需要快速验证高德地图 SDK 在 Android Studio 里怎么接的开发者。它解决的不是「做一个商业级跑步软件」而是「让你在半天内看到一个真实可运行的定位轨迹 Demo 长什么样」。下面按「资源是什么 → 怎么用 → 坑在哪」的顺序拆开讲。2. 环境搭建与 Gradle 配置把项目从压缩包跑到模拟器2.1 为什么这个项目用高德地图而不是 Google Maps国内安卓设备上 Google Play 服务缺失是常态用 Google Maps SDK 的结果就是地图一片空白定位回调也不触发。高德地图 SDK 在国内的定位精度和覆盖度是经过验证的Android_Map3D_SDK_V6.8.0_20190401.jar这个文件名本身就说明了版本——3D 地图、6.8.0、2019 年 4 月 1 日构建。选它不是因为先进而是因为稳。这个 jar 包直接放在项目根目录说明作者没有走 Maven 仓库依赖而是手动集成。手动集成的好处是不依赖网络拉取坏处是你得自己处理 so 库和权限。常见做法是去高德开放平台申请一个 Key然后在AndroidManifest.xml里配com.amap.api.v2.apikey。这个项目里没有 manifest 文件说明它可能只保留了核心逻辑文件你需要自己补一个完整的 Android 项目壳。我一般会先用 Android Studio 新建一个 Empty Activity 项目再把mapActivity.java和 jar 包挪进去。2.2 从零补全项目结构的操作步骤第一步新建项目。Android Studio 里选 Empty Views Activity语言选 Java最低 SDK 建议设到 API 23Android 6.0因为高德 6.8.0 的定位权限在 6.0 以上需要动态申请。第二步放 jar 包。在app模块下新建libs目录把Android_Map3D_SDK_V6.8.0_20190401.jar复制进去然后在app/build.gradle里加一行dependencies { implementation files(libs/Android_Map3D_SDK_V6.8.0_20190401.jar) // 高德地图 6.8.0 还需要对应的 so 库通常放在 jniLibs 下 // 如果只有 jar 没有 so地图会加载失败报 native library not found }第三步配权限。在AndroidManifest.xml里加定位和网络权限uses-permission android:nameandroid.permission.ACCESS_FINE_LOCATION / uses-permission android:nameandroid.permission.ACCESS_COARSE_LOCATION / uses-permission android:nameandroid.permission.INTERNET / uses-permission android:nameandroid.permission.WRITE_EXTERNAL_STORAGE / !-- Android 10 需要后台定位的话还要加 ACCESS_BACKGROUND_LOCATION --第四步在 manifest 的application标签里写 Keymeta-data android:namecom.amap.api.v2.apikey android:value你申请到的高德Key /第五步把mapActivity.java注册进去activity android:name.mapActivity /这五步做完Sync 一下 Gradle如果报Duplicate class或者Program type already present说明你同时引了高德的 Maven 依赖和本地 jar二选一即可。这个项目是本地 jar 路线就把 Maven 那行删掉。2.3 Gradle 版本与 Android Studio 版本的匹配gradle-wrapper.jar和gradlew的存在说明项目用的是 Gradle Wrapper。但 wrapper 的版本写在gradle/wrapper/gradle-wrapper.properties里这个文件不在资源列表中所以你需要自己指定。2019 年的项目大概率用的是 Gradle 5.x 或 6.x对应 Android Studio 3.5 到 4.0。如果你现在用 Android Studio 2023 以后的版本Gradle 8.x 会直接报Unsupported class file major version。我的做法是不纠结原版 Gradle 版本直接用当前 Android Studio 新建项目的 Gradle 配置只把mapActivity.java和 jar 包移植过去。因为 Gradle 配置是壳业务逻辑在 Java 文件里。移植时注意build.gradle里的compileSdkVersion和targetSdkVersion要改成你本地已有的 SDK 版本否则会卡在下载 SDK 那一步。提示如果你在gradlew.bat双击后窗口一闪而过不要慌这是正常的——它需要在命令行里执行而且要在项目根目录下执行gradlew tasks才能看到输出。3. mapActivity 里的定位与轨迹绘制实时速度和路径怎么落地的3.1 定位回调与速度计算的代码逻辑mapActivity.java是这个项目唯一的核心类。它要同时做四件事拿定位、画路径、算速度、存数据。高德 SDK 的定位用法是AMapLocationClient加AMapLocationListener。下面是我根据这个项目的功能描述还原出来的关键代码结构public class mapActivity extends AppCompatActivity implements AMapLocationListener { private AMapLocationClient locationClient; private AMap aMap; private Polyline runningPath; // 跑步路径折线 private ListLatLng pathPoints new ArrayList(); private double currentSpeed 0.0; Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_map); // 初始化地图 MapView mapView findViewById(R.id.map_view); mapView.onCreate(savedInstanceState); aMap mapView.getMap(); // 初始化定位 locationClient new AMapLocationClient(getApplicationContext()); AMapLocationClientOption option new AMapLocationClientOption(); option.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy); option.setInterval(2000); // 2 秒回调一次跑步场景够用 locationClient.setLocationOption(option); locationClient.setLocationListener(this); locationClient.startLocation(); } Override public void onLocationChanged(AMapLocation location) { if (location null || location.getErrorCode() ! 0) return; // 速度高德直接返回 m/s转成 km/h 更符合跑步习惯 currentSpeed location.getSpeed() * 3.6; LatLng newPoint new LatLng(location.getLatitude(), location.getLongitude()); pathPoints.add(newPoint); // 每次回调更新折线 if (runningPath null) { runningPath aMap.addPolyline(new PolylineOptions() .addAll(pathPoints) .width(10) .color(Color.RED)); } else { runningPath.setPoints(pathPoints); } // 地图视角跟随 aMap.moveCamera(CameraUpdateFactory.newLatLngZoom(newPoint, 17)); } }逻辑说明setInterval(2000)是定位回调间隔单位毫秒。跑步时 2 秒一次足够画出平滑路径设成 1000 会明显增加耗电。location.getSpeed()返回的是米每秒乘以 3.6 转成公里每小时这是跑步 App 的通用做法。Polyline是折线每次回调把新点加进列表再setPoints比每次addPolyline更省内存。参数说明Hight_Accuracy是高精度模式会同时用 GPS 和网络定位室内可能漂移如果只测跑步可以改成Device_Sensors只用 GPS但首次定位会慢。newLatLngZoom的第二个参数是缩放级别17 大约能看到街道级别跑步时够用。3.2 跑步履历的数据持久化方案履历管理意味着每次跑步结束要存一条记录包含日期、距离、时长、平均速度、路径点。这个项目没有提到数据库但常见做法是两种SQLite 或文件存储。SQLite 更适合「查看数据详细」这种需要按 ID 查询的场景。我一般会建一张run_record表CREATE TABLE run_record ( id INTEGER PRIMARY KEY AUTOINCREMENT, start_time TEXT NOT NULL, distance REAL DEFAULT 0, duration INTEGER DEFAULT 0, avg_speed REAL DEFAULT 0, path_json TEXT );path_json存的是路径点的 JSON 数组比如[{lat:39.9,lng:116.4},...]。这样查详情时直接反序列化就能在地图上重绘。距离计算用高德AMapUtils.calculateLineDistance(startLatLng, endLatLng)逐段累加不要用经纬度差值乘系数那个误差在长距离下会大到离谱。写入时机是用户点「结束跑步」按钮时把pathPoints转成 JSON算好总距离和总时长插一条记录。查询履历就是SELECT * FROM run_record ORDER BY start_time DESC点某一条再按 ID 查详情。3.3 地图生命周期与 Activity 的绑定高德MapView必须和 Activity 生命周期同步否则会内存泄漏或者黑屏。在mapActivity里要重写onResume、onPause、onDestroy、onSaveInstanceStateOverride protected void onResume() { super.onResume(); mapView.onResume(); } Override protected void onPause() { super.onPause(); mapView.onPause(); } Override protected void onDestroy() { super.onDestroy(); if (locationClient ! null) { locationClient.stopLocation(); locationClient.onDestroy(); } mapView.onDestroy(); }这段代码是血泪经验不写onDestroy里的stopLocation退出页面后定位还在跑用户手机发烫不说后台还会持续耗电。不写mapView.onDestroy()下次进页面地图直接白屏。4. 避坑与常见问题从编译失败到定位漂移的排查清单4.1 编译期报错Duplicate class 和 SDK 版本不匹配现象Sync 后报Duplicate class com.amap.api.maps.AMap found in modules。原因同时引了高德 Maven 依赖和本地 jar。解决检查build.gradle的 dependencies只保留implementation files(libs/Android_Map3D_SDK_V6.8.0_20190401.jar)删掉任何com.amap.api:map3d之类的远程依赖。现象报Unsupported class file major version 61。原因Gradle 版本太低跑不了 Java 17 编译的代码。解决升级gradle-wrapper.properties里的distributionUrl到 Gradle 7.5 以上或者把 Android Studio 的 Gradle JDK 改成 Java 11。4.2 运行期问题地图空白与定位不回调现象App 装上去地图是灰的没有网格也没有路。原因高德 Key 没配或者配错位置。解决确认AndroidManifest.xml的application标签内写了com.amap.api.v2.apikey且 Key 的包名和签名与申请时一致。调试版和发布版签名不同Key 也要分别申请。现象定位回调一直不触发onLocationChanged不执行。原因Android 6.0 以上没有动态申请权限。解决在onCreate里加ActivityCompat.requestPermissions申请ACCESS_FINE_LOCATION用户拒绝后要引导去设置页开权限。另外模拟器上定位可能返回 0,0建议用真机测。4.3 数据层面的坑速度跳变和路径锯齿现象静止时速度显示 3 km/h 以上或者跑步时速度突然跳到 50。原因GPS 漂移导致相邻两个点距离计算异常。解决加一个过滤逻辑如果location.getAccuracy() 50就丢弃这个点或者用滑动平均取最近 3 个点的速度均值。现象路径画出来是锯齿状的不像真实跑步路线。原因定位点太少或者间隔太长。解决把setInterval从 2000 改成 1000同时开启setNeedAddress(false)减少网络请求。但要注意间隔越短耗电越快跑步场景 1 到 2 秒是平衡点。4.4 存储与权限Android 10 以上的分区存储现象保存路径 JSON 到外部存储时报Permission denied。原因Android 10 引入了分区存储WRITE_EXTERNAL_STORAGE不再对Environment.getExternalStorageDirectory()生效。解决把数据存到getExternalFilesDir()或者直接用 SQLite 存内部数据库不需要任何存储权限。这个项目如果只存 SQLite完全可以去掉存储权限。5. 进阶技巧用模拟轨迹验证路径绘制与数据回放真机跑一次只能测一条路线调试路径绘制逻辑时效率很低。我一般会写一个模拟定位的开关用代码生成一条预设轨迹直接喂给onLocationChanged。这样不用出门就能验证折线绘制、速度计算和履历存储是否正常。// 模拟轨迹在测试模式下每 2 秒生成一个点 private void startMockLocation() { final Handler handler new Handler(); final double[][] mockPoints { {39.9042, 116.4074}, {39.9045, 116.4080}, {39.9049, 116.4086}, {39.9053, 116.4092} }; final int[] index {0}; handler.postDelayed(new Runnable() { Override public void run() { if (index[0] mockPoints.length) return; AMapLocation mock new AMapLocation(mock); mock.setLatitude(mockPoints[index[0]][0]); mock.setLongitude(mockPoints[index[0]][1]); mock.setSpeed(3.0f); // 固定 3 m/s约 10.8 km/h mock.setErrorCode(0); onLocationChanged(mock); index[0]; handler.postDelayed(this, 2000); } }, 2000); }这段代码的关键是构造一个AMapLocation对象并手动设置errorCode为 0否则onLocationChanged里的判断会直接 return。setSpeed(3.0f)模拟的是跑步速度你可以改成 0.5 测走路改成 8.0 测骑行。用这个方式十分钟就能把路径绘制、速度显示、数据入库三条链路全跑一遍。验证数据回放时从 SQLite 里读出一条记录的path_json反序列化成ListLatLng再aMap.addPolyline画出来。如果画出来的线和跑步时看到的一致说明存储和读取都没丢点。我习惯在每次改完定位逻辑后先用模拟轨迹跑三遍再拿真机出门跑一次。从那以后我每次接定位相关的需求都强制走一遍「模拟轨迹 → 真机短距离 → 真机长距离」的流程能省下大量在户外反复调试的时间。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站