9、Google Play Services:FusedLocationProviderClient介绍、GoogleApiClient配置、高精度定位实现

说实话,Android 原生的 LocationManager 用起来挺繁琐的。你得自己管 Provider、自己处理权限、还得考虑不同厂商的兼容性。我在早期项目里吃过不少亏,后来全面转向 Google Play Services 的定位方案,才算是真正解放了双手。

这一章,我们就来聊聊 Google Play Services 里的定位核心——FusedLocationProviderClient。它不是什么新东西,但绝对是目前 Android 定位的“标准答案”。

9.1 为什么是 FusedLocationProviderClient?

FusedLocationProviderClient,简称 Fused API。它的核心思想是“融合”。说白了,它把 GPS、Wi-Fi、基站、传感器这些数据源整合在一起,自动帮你选最优的方案。

你想想看,如果只用 GPS,室内基本废了。如果只用基站定位,精度又太差。Fused API 会根据场景自动切换,甚至能通过传感器辅助定位。我在做外卖配送 App 时,就靠它解决了骑手在商场内定位漂移的问题。

核心优势:
  • 自动融合多源定位数据
  • 省电——系统会批量处理定位请求
  • 统一接口,屏蔽厂商差异
  • 支持高精度、低功耗、无 GPS 三种模式

9.2 前置条件:Google Play Services 配置

要用 Fused API,第一步是确保设备上有 Google Play Services。不过别担心,大部分国产手机虽然没装 Google 服务框架,但只要你用 Google 官方渠道发布的 App,用户设备基本都满足。

配置其实很简单,就两步:

  1. 添加依赖:在 build.gradle 里加上 play-services-location
  2. 检查可用性:运行时判断 Google Play Services 是否可用

我个人习惯在 Application 初始化时做一次检查,避免后续定位时突然崩溃。

// build.gradle (Module: app)
dependencies {
    implementation 'com.google.android.gms:play-services-location:21.0.1'
}
// 检查 Google Play Services 是否可用
public boolean isGooglePlayServicesAvailable(Context context) {
    GoogleApiAvailability apiAvailability = GoogleApiAvailability.getInstance();
    int resultCode = apiAvailability.isGooglePlayServicesAvailable(context);
    if (resultCode == ConnectionResult.SUCCESS) {
        return true;
    }
    // 如果不可用,可以提示用户更新或下载
    if (apiAvailability.isUserResolvableError(resultCode)) {
        // 这里可以弹 Dialog 引导用户解决
    }
    return false;
}
注意:Google Play Services 版本号要跟你的 compileSdk 匹配。我遇到过因为版本号写错导致编译失败的情况,排查了半天才发现是版本号太旧了。

9.3 GoogleApiClient 配置(旧方式)

在早期版本中,我们需要通过 GoogleApiClient 来连接 Google Play Services。虽然现在官方推荐直接用 Task API,但很多老项目还在用,所以我还是提一下。

配置流程大概是这样的:

  1. 创建 GoogleApiClient.Builder
  2. 添加 LocationServices.API
  3. 实现 ConnectionCallbacks 和 OnConnectionFailedListener
  4. 在 onConnected 回调里获取 FusedLocationProviderClient
// 旧方式:GoogleApiClient 配置
GoogleApiClient googleApiClient = new GoogleApiClient.Builder(this)
        .addApi(LocationServices.API)
        .addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
            @Override
            public void onConnected(Bundle bundle) {
                // 连接成功,可以获取定位了
                FusedLocationProviderClient fusedClient =
                        LocationServices.getFusedLocationProviderClient(MainActivity.this);
            }

            @Override
            public void onConnectionSuspended(int i) {
                // 连接中断,可以尝试重连
            }
        })
        .addOnConnectionFailedListener(new GoogleApiClient.OnConnectionFailedListener() {
            @Override
            public void onConnectionFailed(ConnectionResult connectionResult) {
                // 连接失败,处理错误
            }
        })
        .build();

googleApiClient.connect();
我的建议:新项目直接用 Task API 吧,GoogleApiClient 已经被标记为废弃了。不过如果你在维护老项目,这个代码还能用,别急着重构。

9.4 新方式:直接使用 FusedLocationProviderClient

从 Google Play Services 11.0.0 开始,官方推荐直接通过 LocationServices 获取 FusedLocationProviderClient 实例。不需要再手动管理连接状态,省心很多。

// 新方式:直接获取 FusedLocationProviderClient
FusedLocationProviderClient fusedLocationClient =
        LocationServices.getFusedLocationProviderClient(this);

就这么一行。你不需要关心连接、不需要回调、不需要生命周期管理。Task API 会自动处理这些。

为什么会这么设计?因为 Google 发现大部分开发者只是想要一个定位结果,并不想管底层连接。所以干脆把复杂性封装起来了。

9.5 高精度定位实现

好了,现在我们来点实际的。高精度定位,说白了就是让系统用尽一切手段获取最准确的位置。对应的策略是 Priority.PRIORITY_HIGH_ACCURACY

实现步骤:

  1. 创建 LocationRequest,设置优先级和更新间隔
  2. 调用 fusedLocationClient.requestLocationUpdates()
  3. 在回调中处理位置数据
  4. 别忘了在 onPause 或 onDestroy 中移除更新
// 创建高精度定位请求
LocationRequest locationRequest = LocationRequest.create()
        .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)  // 高精度
        .setInterval(5000)           // 每5秒更新一次
        .setFastestInterval(2000)    // 最快2秒更新一次
        .setSmallestDisplacement(10); // 最小位移10米才触发更新

// 请求位置更新
fusedLocationClient.requestLocationUpdates(locationRequest,
        new LocationCallback() {
            @Override
            public void onLocationResult(LocationResult locationResult) {
                if (locationResult == null) {
                    return;
                }
                for (Location location : locationResult.getLocations()) {
                    // 处理位置数据
                    double latitude = location.getLatitude();
                    double longitude = location.getLongitude();
                    float accuracy = location.getAccuracy();
                    // 嗯,这里可以更新 UI 或者上传服务器
                }
            }
        },
        Looper.getMainLooper());  // 在主线程回调
关键参数说明:
参数 说明 我的建议
PRIORITY_HIGH_ACCURACY 使用 GPS + 网络 + 传感器,精度最高 导航类 App 必选
setInterval 期望的更新间隔(毫秒) 别设太短,5秒以上比较合理
setFastestInterval 最快更新间隔(毫秒) 防止系统频繁回调
setSmallestDisplacement 最小位移(米) 避免静止时频繁更新

9.6 一次定位 vs 持续定位

不是所有场景都需要持续定位。比如用户点一下“签到”,你只需要获取当前位置一次就够了。这时候用 getCurrentLocation() 更合适。

// 一次定位:适合签到、打卡等场景
Task<Location> locationTask = fusedLocationClient.getCurrentLocation(
        Priority.PRIORITY_HIGH_ACCURACY, null);

locationTask.addOnSuccessListener(location -> {
    if (location != null) {
        // 获取到位置了
        double lat = location.getLatitude();
        double lng = location.getLongitude();
    }
}).addOnFailureListener(e -> {
    // 定位失败,可能是权限问题或 GPS 未开启
    Log.e("Location", "获取位置失败", e);
});

持续定位适合导航、运动轨迹记录。一次定位适合签到、天气查询。我在做考勤 App 时,用的就是 getCurrentLocation,简单高效。

避坑指南:我曾经在 getCurrentLocation 上踩过一个坑——它在某些低端手机上返回 null。原因是系统超时了还没拿到位置。解决方案是加一个 fallback,如果 30 秒内没拿到位置,就改用 requestSingleUpdate() 兜底。

9.7 知识体系图

下面这张图总结了 FusedLocationProviderClient 的核心脉络,从配置到使用,一目了然。

FusedLocationProviderClient 知识体系 FusedLocationProviderClient 配置方式 定位模式 使用方式 GoogleApiClient(旧) Task API(新) 高精度 平衡精度 低功耗 持续定位 一次定位 核心:自动融合多源数据,省电且精准 新项目推荐 Task API + PRIORITY_HIGH_ACCURACY

9.8 权限与运行时检查

定位离不开权限。Android 6.0 以上需要动态申请。高精度定位需要 ACCESS_FINE_LOCATION 权限。

// 动态申请权限
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
        != PackageManager.PERMISSION_GRANTED) {
    ActivityCompat.requestPermissions(this,
            new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
            LOCATION_PERMISSION_REQUEST_CODE);
    return;
}
// 权限已授予,开始定位
startLocationUpdates();

另外,别忘了在 Android 10+ 上,如果要在后台定位,还需要 ACCESS_BACKGROUND_LOCATION 权限。这个权限比较敏感,Google Play 审核也会重点检查。

小技巧:我一般会在申请权限前先判断 shouldShowRequestPermissionRationale(),如果用户之前拒绝过,就弹一个解释说明的 Dialog,告诉用户为什么需要定位权限。这样用户体验会好很多。

9.9 总结

FusedLocationProviderClient 是 Google Play Services 定位方案的核心。它把复杂的底层逻辑封装起来,让你只需要关心定位策略和结果。

配置上,新项目直接用 Task API,别再用 GoogleApiClient 了。定位模式选 PRIORITY_HIGH_ACCURACY 就能拿到最高精度的位置。持续定位用 requestLocationUpdates,一次定位用 getCurrentLocation。

嗯,这一章的内容就到这里。代码不多,但都是实战中反复验证过的。你拿去用,基本不会出问题。


公众号:蓝海资料掘金营,微信deep3321