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,用户设备基本都满足。
配置其实很简单,就两步:
- 添加依赖:在 build.gradle 里加上 play-services-location
- 检查可用性:运行时判断 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;
}
9.3 GoogleApiClient 配置(旧方式)
在早期版本中,我们需要通过 GoogleApiClient 来连接 Google Play Services。虽然现在官方推荐直接用 Task API,但很多老项目还在用,所以我还是提一下。
配置流程大概是这样的:
- 创建 GoogleApiClient.Builder
- 添加 LocationServices.API
- 实现 ConnectionCallbacks 和 OnConnectionFailedListener
- 在 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();
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。
实现步骤:
- 创建 LocationRequest,设置优先级和更新间隔
- 调用 fusedLocationClient.requestLocationUpdates()
- 在回调中处理位置数据
- 别忘了在 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,简单高效。
9.7 知识体系图
下面这张图总结了 FusedLocationProviderClient 的核心脉络,从配置到使用,一目了然。
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 审核也会重点检查。
9.9 总结
FusedLocationProviderClient 是 Google Play Services 定位方案的核心。它把复杂的底层逻辑封装起来,让你只需要关心定位策略和结果。
配置上,新项目直接用 Task API,别再用 GoogleApiClient 了。定位模式选 PRIORITY_HIGH_ACCURACY 就能拿到最高精度的位置。持续定位用 requestLocationUpdates,一次定位用 getCurrentLocation。
嗯,这一章的内容就到这里。代码不多,但都是实战中反复验证过的。你拿去用,基本不会出问题。
公众号:蓝海资料掘金营,微信deep3321