Android开发:如何为FusedLocationClient的requestLocationUpdates添加位置回调?
解决Android 5.1下LocationUpdatesPendingIntent无GPS时无法获取位置及添加获取状态回调的问题
嘿,我刚好碰到过类似的Android版本差异问题!先给你拆解下根源:Android 6.0+的定位框架会自动在GPS不可用时 fallback 到网络定位,但5.1及更早的系统,如果你没明确配置定位优先级,默认可能只依赖GPS提供者。加上你用的是PendingIntent方式接收位置更新,默认没有直接的回调来判断是否获取到有效位置,所以得从两方面入手:适配5.1的定位策略 + 添加自定义回调来检查位置有效性。
下面是一步步的具体实现方案:
1. 先修正LocationRequest的配置,让5.1支持网络定位
首先要确保你的LocationRequest允许使用网络定位,而不是只盯着GPS。把优先级设置为PRIORITY_BALANCED_POWER_ACCURACY或者PRIORITY_LOW_POWER,这样系统会自动尝试网络定位(只要设备开启了网络定位选项):
private void buildLocationRequest() { mLocationRequest = LocationRequest.create(); // 关键:设置支持网络定位的优先级,适配5.1 mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY); mLocationRequest.setInterval(10000); // 10秒一次更新,按需调整 mLocationRequest.setFastestInterval(5000); // 最快更新间隔 mLocationRequest.setSmallestDisplacement(10); // 最小位移触发更新,按需调整 }
2. 添加自定义回调接口,监听位置获取状态
我们需要定义一个回调接口,用来通知调用者位置是否成功获取:
public interface LocationUpdateCallback { // 获取到有效位置时回调 void onLocationReceived(Location location); // 获取失败时回调(比如位置为空、权限不足等) void onLocationFailed(String errorMessage); }
3. 改造广播接收器,检查位置有效性并触发回调
因为你用的是PendingIntent接收位置更新,所以核心逻辑要放在自定义的BroadcastReceiver里,在这里检查返回的Location是否有效,并触发我们的回调:
public class LocationUpdatesReceiver extends BroadcastReceiver { private static final String TAG = "LocationUpdatesReceiver"; private LocationUpdateCallback mCallback; // 构造时传入回调实例 public LocationUpdatesReceiver(LocationUpdateCallback callback) { mCallback = callback; } @Override public void onReceive(Context context, Intent intent) { if (LocationResult.hasResult(intent)) { LocationResult result = LocationResult.extractResult(intent); if (result == null) { mCallback.onLocationFailed("位置结果为空"); return; } Location location = result.getLastLocation(); // 检查经纬度是否有效(避免0,0的默认无效值) if (location != null && location.getLatitude() != 0.0 && location.getLongitude() != 0.0) { mCallback.onLocationReceived(location); } else { mCallback.onLocationFailed("未获取到有效位置,请检查定位设置"); } } else { // 处理定位提供者状态变化(比如用户关闭了所有定位) if (LocationManager.PROVIDERS_CHANGED_ACTION.equals(intent.getAction())) { LocationManager lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); boolean hasValidProvider = lm.isProviderEnabled(LocationManager.GPS_PROVIDER) || lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER); if (!hasValidProvider) { mCallback.onLocationFailed("所有定位提供者均已禁用"); } } } } }
4. 在发起位置请求时添加回调,同时适配权限和设置检查
现在在你的Activity/Service中,初始化回调、注册广播,并发起位置请求,同时添加请求本身的成功/失败监听:
public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; private static final int REQUEST_CHECK_SETTINGS = 1001; private static final String ACTION_LOCATION_UPDATES = "com.your.package.ACTION_LOCATION_UPDATES"; private FusedLocationProviderClient mFusedLocationClient; private LocationRequest mLocationRequest; private LocationUpdatesReceiver mReceiver; // 初始化自定义回调 private LocationUpdateCallback mLocationCallback = new LocationUpdateCallback() { @Override public void onLocationReceived(Location location) { Log.d(TAG, "成功获取位置:" + location.getLatitude() + ", " + location.getLongitude()); // 在这里处理你的位置逻辑 } @Override public void onLocationFailed(String errorMessage) { Log.e(TAG, "位置获取失败:" + errorMessage); // 可以在这里给用户弹提示,比如引导开启定位 } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 初始化广播接收器并注册 mReceiver = new LocationUpdatesReceiver(mLocationCallback); IntentFilter filter = new IntentFilter(); filter.addAction(ACTION_LOCATION_UPDATES); filter.addAction(LocationManager.PROVIDERS_CHANGED_ACTION); registerReceiver(mReceiver, filter); // 初始化FusedLocationClient和LocationRequest mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this); buildLocationRequest(); // 先检查定位设置是否满足要求,再发起请求 checkLocationSettings(); } // 检查定位设置,引导用户开启必要的定位提供者 private void checkLocationSettings() { LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder() .addLocationRequest(mLocationRequest); SettingsClient settingsClient = LocationServices.getSettingsClient(this); Task<LocationSettingsResponse> task = settingsClient.checkLocationSettings(builder.build()); task.addOnSuccessListener(this, response -> { // 定位设置满足,发起位置请求 requestLocationUpdates(); }); task.addOnFailureListener(this, e -> { int statusCode = ((ApiException) e).getStatusCode(); if (statusCode == LocationSettingsStatusCodes.RESOLUTION_REQUIRED) { // 需要用户调整定位设置,弹出系统设置页面 try { ResolvableApiException resolvable = (ResolvableApiException) e; resolvable.startResolutionForResult(this, REQUEST_CHECK_SETTINGS); } catch (IntentSender.SendIntentException sendEx) { mLocationCallback.onLocationFailed("无法打开定位设置页面"); } } else if (statusCode == LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE) { mLocationCallback.onLocationFailed("设备不支持所需的定位设置"); } }); } // 发起位置请求,并添加请求状态监听 private void requestLocationUpdates() { try { Intent intent = new Intent(ACTION_LOCATION_UPDATES); PendingIntent pendingIntent = PendingIntent.getBroadcast( this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE ); // 发起请求,同时监听请求是否成功提交 mFusedLocationClient.requestLocationUpdates(mLocationRequest, pendingIntent) .addOnSuccessListener(v -> Log.d(TAG, "位置更新请求已成功发起")) .addOnFailureListener(e -> { Log.e(TAG, "发起位置请求失败:" + e.getMessage()); mLocationCallback.onLocationFailed("发起请求失败:" + e.getMessage()); }); } catch (SecurityException e) { e.printStackTrace(); mLocationCallback.onLocationFailed("缺少定位权限,请先授权"); } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_CHECK_SETTINGS) { if (resultCode == RESULT_OK) { // 用户已开启定位设置,重新发起请求 requestLocationUpdates(); } else { mLocationCallback.onLocationFailed("用户拒绝开启定位设置"); } } } @Override protected void onDestroy() { super.onDestroy(); // 取消位置请求 if (mFusedLocationClient != null) { Intent intent = new Intent(ACTION_LOCATION_UPDATES); PendingIntent pendingIntent = PendingIntent.getBroadcast( this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE ); mFusedLocationClient.removeLocationUpdates(pendingIntent); } // 注销广播接收器 unregisterReceiver(mReceiver); } // 前面定义的buildLocationRequest方法 private void buildLocationRequest() { mLocationRequest = LocationRequest.create(); mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY); mLocationRequest.setInterval(10000); mLocationRequest.setFastestInterval(5000); mLocationRequest.setSmallestDisplacement(10); } }
5. 别忘了Manifest权限配置
确保在AndroidManifest.xml中添加必要的权限,尤其是5.1需要的网络定位相关权限:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.INTERNET" />
最后补充几点
- 对于Android 5.1,用户必须开启网络定位(不仅仅是GPS)才能在无GPS时获取位置,通过
checkLocationSettings方法可以引导用户去开启。 - 你可以根据需求调整
LocationRequest的间隔、位移参数,平衡功耗和更新频率。 - 如果应用进程被杀后仍要保持位置更新,确保你的PendingIntent是用
FLAG_IMMUTABLE(Android 12+)或合适的标志,并且FusedLocationProvider会在进程重启后继续发送广播。
内容的提问来源于stack exchange,提问作者Code Spy
相关产品推荐
相关产品推荐

