Android中基于道路获取经纬度间距离的实现(血库APP场景)
如何使用Google Directions API获取准确的道路距离
首先得明确:你之前用的SphericalUtil.computeDistanceBetween和Location.distanceBetween都是计算直线(球面)距离,这和实际道路距离肯定有差距,尤其是城市里或者地形复杂的区域,差几十公里很正常。要获取真实的道路距离,Google Directions API确实是目前最靠谱的方案,下面我一步步教你正确实现:
一、先搞定API的前置准备(必须步骤)
你在Postman遇到的启用API和计费要求是Google的官方政策,没法跳过,但不用太担心成本:
- 启用Directions API:登录Google Cloud Console,找到你的项目,搜索「Directions API」,点击启用即可。
- 设置计费账户:绑定支付方式是必须的,但Google给每个账户每月提供一定额度的免费请求(具体额度可以看Cloud Console的定价页面),个人开发的小流量APP完全够用,超出免费额度才会扣费。
- 获取API密钥:在Cloud Console的「API和服务」→「凭据」页面,创建一个API密钥,记得限制密钥的使用范围(比如只允许Android应用调用,避免被滥用)。
二、在APP中实现道路距离查询
1. 构造API请求
Directions API的请求格式是这样的(HTTP GET):
https://maps.googleapis.com/maps/api/directions/json?origin=USER_LAT,USER_LNG&destination=DONOR_LAT,DONOR_LNG&mode=driving&key=YOUR_API_KEY
参数说明:
origin:用户的当前经纬度(比如currentLat,currentLong)destination:献血者的经纬度(donarLat,donarLong)mode:可选值driving(驾车)、walking(步行)、bicycling(骑行),根据你的APP场景选择key:你刚才获取的API密钥
2. 发送异步请求(避免APP崩溃)
绝对不能在主线程发送网络请求,否则会直接ANR甚至崩溃。推荐用OkHttp来做异步请求,先添加依赖到build.gradle:
implementation 'com.squareup.okhttp3:okhttp:4.11.0'
然后编写核心请求代码:
// 替换成你的API密钥 private static final String API_KEY = "YOUR_GOOGLE_API_KEY"; private OkHttpClient client = new OkHttpClient(); // 定义方法获取道路距离 private void getRoadDistance(double userLat, double userLng, double donorLat, double donorLng, final DistanceCallback callback) { String url = String.format("https://maps.googleapis.com/maps/api/directions/json?origin=%f,%f&destination=%f,%f&mode=driving&key=%s", userLat, userLng, donorLat, donorLng, API_KEY); Request request = new Request.Builder().url(url).build(); client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { // 请求失败,回调通知 callback.onFailure(e.getMessage()); } @Override public void onResponse(Call call, Response response) throws IOException { if (!response.isSuccessful()) { callback.onFailure("请求失败:" + response.code()); return; } String responseBody = response.body().string(); // 解析JSON响应 try { JSONObject jsonObject = new JSONObject(responseBody); String status = jsonObject.getString("status"); if ("OK".equals(status)) { JSONArray routes = jsonObject.getJSONArray("routes"); JSONObject firstRoute = routes.getJSONObject(0); JSONArray legs = firstRoute.getJSONArray("legs"); JSONObject firstLeg = legs.getJSONObject(0); JSONObject distanceObj = firstLeg.getJSONObject("distance"); // 距离值是米,转换成公里 double distanceInMeters = distanceObj.getDouble("value"); double distanceInKm = distanceInMeters / 1000; // 切回主线程更新UI new Handler(Looper.getMainLooper()).post(() -> { callback.onSuccess(String.format("%.1f km", distanceInKm)); }); } else { callback.onFailure("API返回错误:" + status); } } catch (JSONException e) { callback.onFailure("解析响应失败:" + e.getMessage()); } } }); } // 定义回调接口处理结果 interface DistanceCallback { void onSuccess(String distance); void onFailure(String errorMsg); }
3. 在原有逻辑中调用该方法
替换你原来计算直线距离的代码,换成调用上面的方法:
if (currentLat != null && currentLong != null && donarLat != null && donarLong != null) { getRoadDistance(currentLat, currentLong, donarLat, donarLong, new DistanceCallback() { @Override public void onSuccess(String distance) { // 拿到道路距离,更新UI或打印日志 System.out.println("d9" + profiles.getName() + " : " + distance); } @Override public void onFailure(String errorMsg) { // 请求失败时,用直线距离作为备选方案 float[] result = new float[1]; Location.distanceBetween(currentLat, currentLong, donarLat, donarLong, result); double fallbackDistance = result[0] / 1000; String fallbackStr = String.format("%.1f km(直线)", fallbackDistance); System.out.println("d9" + profiles.getName() + " : " + fallbackStr); } }); }
三、关键注意事项
- API密钥安全:不要把API密钥硬编码在客户端代码里,最好搭建一个简单的后端接口,由后端转发Directions API请求,避免密钥被反编译窃取。
- 错误处理:一定要处理请求失败、API返回错误(比如
NOT_FOUND表示没有路线)的情况,用直线距离作为 fallback,提升用户体验。 - 请求频率限制:Google Directions API有请求频率限制,不要短时间内批量请求太多,否则会被限流,可以考虑缓存查询结果。
这样实现后,你就能准确获取到实际道路距离,也能避免之前的崩溃问题啦。
内容的提问来源于stack exchange,提问作者user12556987
相关产品推荐
相关产品推荐

