如何计算两个地点间的距离?固定地点与用户位置测距咨询
计算固定地点与用户当前位置距离的实现优化建议
嘿,我看了你这段用来计算距离的CalDist AsyncTask代码,能感觉到你想实现固定地点到用户当前位置的距离测算,但目前代码还不完整,而且有个关键问题得注意——AsyncTask在Android API 30及以上已经被官方废弃啦!我给你整理一套更现代、完整的实现方案,不管是算直线距离还是实际导航距离都能用~
核心实现思路
我们可以用Kotlin协程替代过时的AsyncTask,同时提供两种实用的距离计算方式:
- 直线距离(用Haversine公式,无需调用第三方API)
- 实际导航距离(调用Google Maps Directions API,支持步行/驾车/骑行)
1. 先完成权限准备
首先要在AndroidManifest.xml中添加位置权限,确保能获取用户当前位置:
<!-- 粗略位置权限 --> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <!-- 精确定位权限 --> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <!-- 后台定位权限(如果需要后台持续获取位置) --> <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" android:maxSdkVersion="32" />
2. 获取用户当前位置
用Google推荐的FusedLocationProviderClient来获取当前位置,比自己管理位置监听更稳定:
private fun getCurrentLocation(): Task<Location> { val fusedLocationClient = LocationServices.getFusedLocationProviderClient(this) return fusedLocationClient.lastLocation }
3. 计算直线距离(无需API)
如果只需要两点间的直线距离,直接用Haversine公式计算就行,不用调用外部接口:
// 计算两个坐标间的直线距离,单位:米 fun calculateStraightDistance(lat1: Double, lon1: Double, lat2: Double, lon2: Double): Double { val earthRadius = 6371000.0 // 地球半径,单位米 val dLat = Math.toRadians(lat2 - lat1) val dLon = Math.toRadians(lon2 - lon1) val a = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)) * Math.sin(dLon/2) * Math.sin(dLon/2) val c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)) return earthRadius * c }
4. 获取实际导航距离(调用API)
如果需要道路导航的实际距离,就调用Google Maps Directions API,用协程处理网络请求避免阻塞主线程:
// 替换成你在Google Cloud Console申请的API Key private const val GOOGLE_MAPS_API_KEY = "YOUR_API_KEY_HERE" suspend fun getNavigationDistance(originLat: Double, originLon: Double, destLat: Double, destLon: Double): String? { val requestUrl = "https://maps.googleapis.com/maps/api/directions/json?" + "origin=$originLat,$originLon" + "&destination=$destLat,$destLon" + "&mode=driving" // 可选模式:walking/bicycling/transit "&key=$GOOGLE_MAPS_API_KEY" return try { val response = OkHttpClient().newCall(Request.Builder().url(requestUrl).build()).await() if (response.isSuccessful) { val jsonResponse = response.body?.string() ?: return "数据解析失败" val jsonObject = JSONObject(jsonResponse) val routes = jsonObject.getJSONArray("routes") if (routes.length() > 0) { val legs = routes.getJSONObject(0).getJSONArray("legs") legs.getJSONObject(0).getJSONObject("distance").getString("text") } else { "无可用路线" } } else { "请求失败" } } catch (e: Exception) { e.printStackTrace() "计算出错" } }
5. 完整调用流程(协程版)
在Activity中用协程启动整个流程,同时处理权限检查和UI更新:
lifecycleScope.launch { // 先检查位置权限 if (ContextCompat.checkSelfPermission(this@YourActivity, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this@YourActivity, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), 100) return@launch } // 获取当前位置 val currentLocation = getCurrentLocation().await() currentLocation?.let { // 替换成你的固定地点坐标(示例:北京天安门) val fixedLat = 39.9042 val fixedLon = 116.4074 // 计算直线距离并格式化 val straightDistance = calculateStraightDistance(it.latitude, it.longitude, fixedLat, fixedLon) val straightDistanceText = String.format("%.2f米", straightDistance) // 获取导航距离 val navigationDistanceText = getNavigationDistance(it.latitude, it.longitude, fixedLat, fixedLon) // 更新UI(回到主线程操作) runOnUiThread { tvStraightDistance.text = "直线距离:$straightDistanceText" tvNavDistance.text = "驾车距离:${navigationDistanceText ?: "获取失败"}" } } ?: run { runOnUiThread { Toast.makeText(this@YourActivity, "无法获取当前位置", Toast.LENGTH_SHORT).show() } } }
额外注意事项
- 记得在Google Cloud Console中启用Directions API,并申请有效的API Key
- 协程需要添加依赖:
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4" - OkHttp依赖:
implementation "com.squareup.okhttp3:okhttp:4.10.0"
内容的提问来源于stack exchange,提问作者Jagdish Choudhary




