定位功能调试记录(RouteTracker App)

📅 日期

2025年4月3日

🧪 目标

实现 Android App 中基于 Google Maps 的实时定位功能,使用 FusedLocationProviderClient 获取设备位置并在地图上展示。


🧭 过程记录

✅ 地图与定位功能初步实现

  • 使用 GoogleMap 成功加载地图;
  • 使用 FusedLocationProviderClient 调用 requestLocationUpdates
  • 模拟器设置权限和配置正确;
  • 日志打印如下:
    1
    2
    3
    4
    5
    MAP: 地图准备好了
    MAP: 权限检查通过
    MAP: 准备启动 requestLocationUpdates
    MAP: 已经调用 requestLocationUpdates
    MAP: lastLocation 为 null

🛑 问题一:无定位更新

现象

  • 地图加载正常,但始终没有位置回调;
  • 日志中 lastLocation == null
  • 手动发送定位无效;
  • adb shell dumpsys location 显示所有 provider 都是 OFF 或无有效定位。

原因

  • 使用的系统镜像为 x86_64 + API 36,但未正确配置模拟定位模块;
  • Extended Controls > Location 无法控制 GPS;
  • 模拟器系统无 Google APIs 或定位模拟能力未启用。

解决方案

  • 重新创建模拟器,选择 带 Google Play 或 Google APIs 的系统镜像
  • 确认 AVD 为 x86_64 架构;
  • 启动新模拟器后,adb shell dumpsys location 显示 fused provider 正常工作;
  • 使用 Extended Controls > Location 成功发送模拟坐标,北京坐标展示成功 ✅。

🛑 问题二:App 闪退 onDestroy()

错误信息

1
2
Caused by: java.lang.IllegalStateException: FragmentManager has been destroyed
at com.example.routetracker.MapsActivity.onDestroy(MapsActivity.kt:151)

原因

  • App 页面销毁后,仍尝试操作 fragment 或未清理 LocationCallback

解决方案

MapsActivity.ktonDestroy() 中添加:

1
2
3
4
override fun onDestroy() {
super.onDestroy()
fusedLocationClient.removeLocationUpdates(locationCallback)
}

✅ 最终状态

  • 地图成功加载;
  • 蓝点与精度圆在北京显示;
  • 无闪退;
  • 模拟器定位稳定更新。

🧩 关键代码片段

初始化 fusedLocationClient

1
2
3
4
5
6
private lateinit var fusedLocationClient: FusedLocationProviderClient

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
}

配置 LocationRequest 和监听器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
val locationRequest = LocationRequest.Builder(
Priority.PRIORITY_HIGH_ACCURACY, 3000
)
.setMinUpdateDistanceMeters(1f)
.setGranularity(Granularity.GRANULARITY_FINE)
.setWaitForAccurateLocation(false)
.build()

val locationCallback = object : LocationCallback() {
override fun onLocationResult(result: LocationResult) {
for (location in result.locations) {
Log.d("MAP", "📍 新位置:${location.latitude}, ${location.longitude}")
// 更新地图上的蓝点
}
}
}

启动监听

1
2
3
4
5
6
7
8
9
10
11
val task = fusedLocationClient.requestLocationUpdates(
locationRequest,
locationCallback,
Looper.getMainLooper()
)

task.addOnSuccessListener {
Log.d("MAP", "✅ requestLocationUpdates 注册成功")
}.addOnFailureListener {
Log.e("MAP", "❌ 注册失败:${it.message}", it)
}

onDestroy 清理监听

1
2
3
4
override fun onDestroy() {
super.onDestroy()
fusedLocationClient.removeLocationUpdates(locationCallback)
}

📌 建议

  • 后续定位采样建议使用高精度 + 合理间隔(如 3 秒 + 最小 1 米);
  • 可以记录位置轨迹为 LatLng 列表,绘制 Polyline;
  • 页面销毁时清理所有监听资源;
  • 推荐使用 lifecycle-aware API(如 LifecycleServiceLocationComponent)进行封装。