You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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);
        }
    });
}

三、关键注意事项

  1. API密钥安全:不要把API密钥硬编码在客户端代码里,最好搭建一个简单的后端接口,由后端转发Directions API请求,避免密钥被反编译窃取。
  2. 错误处理:一定要处理请求失败、API返回错误(比如NOT_FOUND表示没有路线)的情况,用直线距离作为 fallback,提升用户体验。
  3. 请求频率限制:Google Directions API有请求频率限制,不要短时间内批量请求太多,否则会被限流,可以考虑缓存查询结果。

这样实现后,你就能准确获取到实际道路距离,也能避免之前的崩溃问题啦。

内容的提问来源于stack exchange,提问作者user12556987

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 09:09:55