You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Android定位服务在特定设备每2秒重复更新问题排查

问题分析与解决方案

首先,咱们来拆解你遇到的这个定位重复触发的问题:

为什么部分设备会每2秒重复获取定位?

你在调用mlocManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, mlocListener, looper)时设置的时间间隔和距离间隔都是0,这在不同Android版本和厂商定制ROM里的行为差异很大:

  • 在Android 6.0(API 23)及更早的系统中,当这两个参数为0时,GPS模块会以硬件支持的最高频率返回位置更新。像三星Galaxy S6这类设备的定制ROM,把这个“最高频率”设定成了每2秒一次。
  • 更关键的是,你在onLocationChanged里的操作顺序有问题:先调用了handlerThread.quit(),再尝试移除定位监听。这可能导致Looper已经退出,移除监听的代码没被系统正确执行,后续的定位更新还会持续触发。而Android 7.0+系统对定位服务的调度做了优化,所以你的S7表现正常。

现有代码的快速修复方案

如果你暂时不想迁移到Google Play定位API,可以按以下步骤修改现有代码:

  1. 调整定位请求参数
    如果你只需要一次定位,优先使用requestSingleUpdate(虽然API 29已弃用,但对Android 6+仍有效),或者把时间间隔设为较大的值(比如10000ms)、距离间隔设为100m,避免过度频繁的更新。

  2. 修正onLocationChanged的操作顺序
    先移除定位监听,再退出线程,最后停止服务,确保移除操作被正确执行:

    @Override
    public void onLocationChanged(Location loc) {
        mLoc = loc;
        // 保留你的日志、网络请求、Toast代码...
    
        // 第一步:先移除定位监听,阻断后续更新
        try {
            mlocManager.removeUpdates(mlocListener);
        } catch(Exception e){
            Log.e(TAG, "Failed to remove location updates", e);
        }
        Log.e(TAG, "removed updates(TrackingService)");
    
        // 第二步:安全退出线程(quitSafely比quit更可靠,确保当前任务完成)
        handlerThread.quitSafely();
    
        // 第三步:停止服务
        TrackingService.this.stopSelf();
        Log.e(TAG, "called stopSelf on TrackingService");
    }
    

迁移到Google Play Services Location API(推荐)

Google的Fused Location Provider API是官方推荐的定位方案,兼容性更好,还能自动切换GPS/网络定位,并且可以明确指定只获取一次定位。以下是在Service中实现的示例:

1. 添加依赖

在你的app模块的build.gradle中添加:

implementation 'com.google.android.gms:play-services-location:21.0.1'

2. 实现FusedTrackingService

public class FusedTrackingService extends Service {
    private static final String TAG = FusedTrackingService.class.getSimpleName();
    private FusedLocationProviderClient fusedLocationClient;
    private LocationCallback locationCallback;
    private AppObj appObj;

    @Override
    public void onCreate() {
        super.onCreate();
        fusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
        appObj = (AppObj) getApplication();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.e(TAG, "inside fused tracking service onStartCommand");
        requestSingleLocationUpdate();
        // 设置45秒超时,防止一直等待定位
        new Handler().postDelayed(this::endServiceWithNoLocation, 45 * 1000);
        return START_NOT_STICKY;
    }

    private void requestSingleLocationUpdate() {
        LocationRequest locationRequest = LocationRequest.create();
        locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
        locationRequest.setNumUpdates(1); // 核心:明确只请求一次定位更新
        locationRequest.setInterval(0);
        locationRequest.setFastestInterval(0);

        locationCallback = new LocationCallback() {
            @Override
            public void onLocationResult(LocationResult locationResult) {
                if (locationResult == null || locationResult.getLocations().isEmpty()) {
                    endServiceWithNoLocation();
                    return;
                }
                Location loc = locationResult.getLocations().get(0);
                Log.e(TAG, "got location: " + loc.getLatitude() + ", " + loc.getLongitude());

                // 处理定位数据,发送到Web服务
                DateTime dt = new DateTime(DateTimeZone.UTC);
                DateTimeFormatter df3 = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss.SSS")
                        .withLocale(Locale.UK)
                        .withOffsetParsed();
                String formattedNowTime3 = df3.print(dt);
                appObj.webService.sendUserLocation(formattedNowTime3,
                        String.valueOf(loc.getLatitude()),
                        String.valueOf(loc.getLongitude()));

                // 显示Toast
                new Handler(Looper.getMainLooper()).post(() ->
                        Toast.makeText(getApplicationContext(),
                                "co-ods = " + loc.getLatitude() + " " + loc.getLongitude(),
                                Toast.LENGTH_LONG).show());

                // 清理资源并停止服务
                fusedLocationClient.removeLocationUpdates(locationCallback);
                stopSelf();
            }
        };

        // 检查定位权限(需提前在Manifest声明并请求运行时权限)
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
                == PackageManager.PERMISSION_GRANTED) {
            fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper());
        } else {
            endServiceWithNoLocation();
        }
    }

    private void endServiceWithNoLocation() {
        DateTime dt = new DateTime();
        DateTimeFormatter df3 = DateTimeFormat.forPattern("yyyy-MM-dd H:mm:ss.SSS");
        String formattedNowTime3 = df3.print(dt);
        appObj.webService.sendUserLocation(formattedNowTime3, "NO_LAT", "NO_LON");
        
        if (locationCallback != null) {
            fusedLocationClient.removeLocationUpdates(locationCallback);
        }
        stopSelf();
        Log.e(TAG, "service ended with no location");
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (locationCallback != null) {
            fusedLocationClient.removeLocationUpdates(locationCallback);
        }
    }
}

这个示例中,setNumUpdates(1)会告诉定位服务只返回一次定位结果,从根本上避免了重复触发的问题,同时兼容性覆盖绝大多数Android设备。


内容的提问来源于stack exchange,提问作者turtleboy

火山引擎 最新活动