10、Fused定位API:创建GoogleApiClient、连接与断开生命周期、获取最后一次已知位置
好,咱们今天来聊聊Fused定位API。说实话,这是Android定位开发里最核心的一块。Google推出Fused Location Provider,目的就是帮开发者省心——它自动帮你权衡功耗和精度,你只需要调用几个方法就行。
我个人习惯,只要项目里需要定位,第一反应就是用Fused API。为什么?因为它背后是Google Play服务在调度,会综合Wi-Fi、基站、GPS多种信号源。你想想看,自己写一套定位策略,既要管不同Android版本的兼容,又要处理各种传感器数据,那得多累。
10.1 创建GoogleApiClient
在Fused API的早期版本里,我们需要先创建GoogleApiClient。虽然现在Google推荐用GoogleApi或者Task的方式,但很多老项目还在用这套模式。我建议你两种都了解,毕竟面试时经常被问到。
创建GoogleApiClient,说白了就是告诉系统:我要用Google Play服务里的哪些功能。代码其实很固定:
// 创建GoogleApiClient
GoogleApiClient googleApiClient = new GoogleApiClient.Builder(context)
.addApi(LocationServices.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
这里要注意几个点:
addApi(LocationServices.API)是必须的,表示我们要用定位服务addConnectionCallbacks监听连接成功/断开addOnConnectionFailedListener处理连接失败
.addApi(ActivityRecognition.API)。但别加太多,每个API都会增加连接耗时。
10.2 连接与断开生命周期
嗯,这里要重点讲。GoogleApiClient的生命周期管理,是很多新手容易踩坑的地方。我在项目中遇到过好几次,因为忘记在Activity的onStop()里断开连接,导致内存泄漏。
正确的做法是这样的:
@Override
protected void onStart() {
super.onStart();
// 连接GoogleApiClient
if (googleApiClient != null && !googleApiClient.isConnected()) {
googleApiClient.connect();
}
}
@Override
protected void onStop() {
super.onStop();
// 断开连接,释放资源
if (googleApiClient != null && googleApiClient.isConnected()) {
googleApiClient.disconnect();
}
}
为什么要在onStart()里连接,而不是onCreate()?因为onCreate()只调用一次,而onStart()在Activity每次可见时都会调用。你想想看,如果用户按了Home键再回来,onStart()会重新执行,这时候重新连接是最合理的。
onCreate()里调用connect(),然后在onDestroy()里disconnect()。我曾经见过一个项目这么写,结果Activity被重建时,旧的GoogleApiClient还没断开,新的又连上了,直接导致定位回调混乱。
连接成功的回调长这样:
@Override
public void onConnected(Bundle bundle) {
// 连接成功,可以开始定位了
Log.d(TAG, "GoogleApiClient connected");
// 这里可以获取最后一次已知位置
}
@Override
public void onConnectionSuspended(int cause) {
// 连接中断,比如Google Play服务更新
Log.d(TAG, "Connection suspended, cause: " + cause);
// 建议重新连接
googleApiClient.connect();
}
@Override
public void onConnectionFailed(ConnectionResult result) {
// 连接失败,检查Google Play服务版本
Log.e(TAG, "Connection failed: " + result.getErrorCode());
// 可以提示用户更新Google Play服务
}
这里有个细节:onConnectionSuspended 和 onConnectionFailed 不一样。前者是临时中断,可以自动恢复;后者是永久失败,需要用户干预。我一般会在onConnectionSuspended里直接调connect()重试,而在onConnectionFailed里弹对话框让用户处理。
10.3 获取最后一次已知位置
连接成功后,第一件事通常是获取最后一次已知位置。这个接口非常快,因为它不发起新的定位请求,只是从缓存里拿数据。
// 获取最后一次已知位置
Location lastLocation = LocationServices.FusedLocationApi
.getLastLocation(googleApiClient);
if (lastLocation != null) {
double latitude = lastLocation.getLatitude();
double longitude = lastLocation.getLongitude();
long time = lastLocation.getTime();
// 处理位置数据
} else {
// 没有缓存位置,需要发起新的定位请求
Log.d(TAG, "Last location is null");
}
你可能会问:为什么有时候返回null?说白了,就是设备上没有任何App请求过定位,或者定位被关闭了。我在测试时遇到过这种情况,第一次安装App,打开定位权限后立刻调用getLastLocation(),结果就是null。因为系统还没缓存任何位置数据。
这时候怎么办?我的做法是:如果返回null,就发起一次单次定位请求。后面章节会详细讲,这里先提一下思路。
getLastLocation()是同步方法,必须在非UI线程调用?不对,它内部已经处理好了,可以直接在主线程调用。- 返回的位置可能比较旧,建议检查
getTime(),如果超过5分钟,可以考虑重新请求。 - 精度取决于上次定位使用的Provider,可能是GPS高精度,也可能是Wi-Fi粗略定位。
10.4 完整流程的SVG图
下面这张图,是我自己总结的Fused定位API核心流程。你看一眼就能明白整个生命周期:
10.5 避坑指南
最后,分享几个我踩过的坑:
- Google Play服务版本问题: 如果用户设备上的Google Play服务版本太低,
connect()会直接失败。我建议在onConnectionFailed里检查result.getErrorCode(),如果是SERVICE_VERSION_UPDATE_REQUIRED,就引导用户更新。 - 权限检查: 调用
getLastLocation()前,一定要确保有ACCESS_FINE_LOCATION或ACCESS_COARSE_LOCATION权限。我曾经在Android 6.0以上设备上忘记动态申请权限,结果一直返回null,排查了半天。 - 不要在主线程做耗时操作: 虽然
getLastLocation()本身很快,但如果你在onConnected里做大量计算,会阻塞UI线程。我一般会开一个Handler或者用AsyncTask来处理位置数据。
LocationRequest + FusedLocationProviderClient的方式。GoogleApiClient在较新版本里已经被标记为deprecated了。不过理解它的原理,对你理解整个定位体系非常有帮助。
好了,这一章的内容就到这里。记住,GoogleApiClient的核心就是:创建、连接、使用、断开。把这个生命周期刻在脑子里,后面学什么定位请求都轻松。