targetSdkVersion 30下应用后台/关闭时如何固定10分钟间隔定位避免频繁上报
问题根源分析
- 你设置了
requestLocationUpdates的距离触发阈值为100米,只要设备移动超过100米,不管是否到10分钟间隔,系统都会触发onLocationChanged回调,这是移动时频繁上报的核心原因 - 你在
onLocationChanged里每次收到位置就新建一个Timer,多次回调就会生成多个Timer实例同时运行,进一步加剧了频繁上报的问题 - 整体逻辑混乱:既用了前台服务的Timer做10分钟触发,又在位置回调里重复开Timer,还持续监听位置变化,三者叠加导致上报间隔完全失控
修复方案
1. 调整位置监听逻辑,避免移动触发回调
不要持续注册位置监听,改为每10分钟主动拉取一次位置,拉取到有效位置后立即关闭监听,从根源上避免移动时的频繁回调。
修改LocationMonitoringService的onStartTacking方法:
public void onStartTacking() { LocationManager locationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE); try { // 先尝试拿最近的缓存位置,有有效位置直接上报 Location lastKnownGps = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER); Location lastKnownNetwork = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER); Location bestLocation = getBestLocation(lastKnownGps, lastKnownNetwork); if (bestLocation != null && bestLocation.getAccuracy() < 100) { // 精度小于100米直接用 handleLocation(bestLocation); return; } // 缓存位置不可用,再注册单次位置更新 Criteria criteria = new Criteria(); criteria.setAccuracy(Criteria.ACCURACY_FINE); String bestProvider = locationManager.getBestProvider(criteria, true); if (bestProvider != null) { // 这里把距离设为Integer.MAX_VALUE,只拿一次就取消监听 locationManager.requestLocationUpdates(bestProvider, 0, Integer.MAX_VALUE, this); // 加个超时,10秒没拿到位置就取消,避免空等耗电 new Handler(Looper.getMainLooper()).postDelayed(() -> { locationManager.removeUpdates(this); }, 10000); } } catch (SecurityException e) { e.printStackTrace(); } } // 新增方法:选择最优的缓存位置 private Location getBestLocation(Location gpsLoc, Location networkLoc) { if (gpsLoc == null) return networkLoc; if (networkLoc == null) return gpsLoc; long timeDiff = gpsLoc.getTime() - networkLoc.getTime(); if (timeDiff > 2 * 60 * 1000) return gpsLoc; // gps位置新2分钟以上用gps else if (timeDiff < -2 * 60 * 1000) return networkLoc; // 网络位置新2分钟以上用网络 else return gpsLoc.getAccuracy() < networkLoc.getAccuracy() ? gpsLoc : networkLoc; } // 新增方法:统一处理拿到的位置 private void handleLocation(Location location) { Prefs.putString(AUtils.LAT, String.valueOf(location.getLatitude())); Prefs.putString(AUtils.LONG, String.valueOf(location.getLongitude())); if (Prefs.getBoolean(AUtils.PREFS.IS_ON_DUTY, false)) { sendLocation(); } }
2. 修改onLocationChanged逻辑,删除多余Timer
@Override public void onLocationChanged(Location location) { if (location == null) return; // 拿到位置立即取消监听,避免后续移动触发回调 LocationManager locationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE); locationManager.removeUpdates(this); if (location.getAccuracy() > 100) return; // 精度差的位置直接丢弃 handleLocation(location); }
3. 清理冗余逻辑
- 删掉
LocationMonitoringService里所有和Timer、TimerTask相关的代码,10分钟触发逻辑完全由前台服务的Timer统一管控,避免多定时器冲突 - 前台服务的Timer逻辑不用修改,保持每10分钟调用一次
monitoringService.onStartTacking()即可
4. 可选优化(提升后台稳定性)
如果出现后台Timer不准的情况,可以把Timer换成AlarmManager或者WorkManager,针对targetSdk30的前台服务场景,稳定性更高。注意如果用PendingIntent,Android 12及以上需要加FLAG_IMMUTABLE或者FLAG_MUTABLE标识。
内容的提问来源于stack exchange,提问作者gopssays
相关产品推荐
相关产品推荐

