You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

蓝牙断开时LocationManager获取位置过慢,如何10秒内获取?

蓝牙断开时获取位置耗时过长,如何实现10秒内获取有效位置?

问题背景

驾车到家断开车载蓝牙时,触发位置获取逻辑,但OnLocationChanged延迟超8分钟才触发;在家测试蓝牙耳机断开时无此问题。当前使用NetworkProvider,切换GPSProvider也存在同样问题。

相关实现代码

[BroadcastReceiver]
[IntentFilter(new[] { BluetoothDevice.ActionAclDisconnected })]
public class BluetoothReceiver : BroadcastReceiver
{
    public async override void OnReceive(Context context, Intent intent)
    {
        Log2("On bluetooth receive");
        Log2("Intent is: " + intent.Action);

        if (BluetoothDevice.ActionAclDisconnected.Equals(intent.Action))
        {
            BluetoothDevice device = (BluetoothDevice)intent.GetParcelableExtra(BluetoothDevice.ExtraDevice);
            string deviceName = device.Name;

            Log2("Device name is: " + deviceName);

            if (device.Name.ToUpper().IndexOf("FOO") != -1)
            {
                Log2("Getting location");
                var loc = await GetLocation();

                if (loc == null || loc.HasValue == false)
                {
                    Log2("Location is null or does not have value");
                    return;
                }

                Log2("Location is: " + JsonConvert.SerializeObject(loc.Value));
            }
        }
    }
}

static Location latestLocation = null;

private async static Task<(double lat, double lon)?> GetLocation()
{
    LocationManager locationManager = (LocationManager)_this.GetSystemService(LocationService);

    string provider = LocationManager.NetworkProvider;

    if (locationManager.IsProviderEnabled(provider) == false)
    {
        Log2("Provider: " + provider + " was not available");
        provider = LocationManager.GpsProvider;

        if (locationManager.IsProviderEnabled(provider) == false)
        {
            Log2("Provider: " + provider + " was not available");
            provider = locationManager.GetBestProvider(new Criteria(), true);

            if (provider == null)
            {
                Log2("Best provider was not available, returning");
                return null;
            }
        }
    }

    Log2("Requested location update and waiting");
    latestLocation = null;

    locationManager.RequestLocationUpdates(provider, 30000, 1, _this);

    Stopwatch sw = new Stopwatch();

    sw.Start();

    while (latestLocation == null && sw.ElapsedMilliseconds < 30000)
        await Task.Delay(100);

    if (latestLocation == null)
    {
        Log2("Latest location was null, returning");
        return null;
    }

    double latitude = latestLocation.Latitude;
    double longitude = latestLocation.Longitude;

    return (latitude, longitude);
}

public void OnLocationChanged(Location location)
{
    Log2("location changed event.");

    latestLocation = location;
    
    Log2($"Latitude: {location.Latitude}, Longitude: {location.Longitude}");
}

日志信息

[2/21/2024 5:24 PM 22674ms] On bluetooth receive
[2/21/2024 5:24 PM 0019ms] Intent is: android.bluetooth.device.action.ACL_DISCONNECTED
[2/21/2024 5:24 PM 0026ms] Device name is: FOO
[2/21/2024 5:24 PM 0021ms] Getting location
[2/21/2024 5:24 PM 0034ms] Requested location update and waiting
[2/21/2024 5:25 PM 42009ms] Latest location was null, returning
[2/21/2024 5:25 PM 0048ms] Location is null or does not have value
[2/21/2024 5:33 PM 507948ms] location changed event.
[2/21/2024 5:33 PM 0015ms] Latitude: 123.2584486, Longitude: 456.1175657

优化方案

1. 优先获取缓存位置

在请求新位置前,先尝试获取系统缓存的最近有效位置,避免从头等待:

// 在GetLocation方法开头添加缓存位置判断
private async static Task<(double lat, double lon)?> GetLocation()
{
    LocationManager locationManager = (LocationManager)_this.GetSystemService(LocationService);
    string provider = LocationManager.NetworkProvider;

    // 先尝试获取缓存位置
    var cachedLocation = locationManager.GetLastKnownLocation(provider);
    if (cachedLocation != null && IsLocationValid(cachedLocation))
    {
        return (cachedLocation.Latitude, cachedLocation.Longitude);
    }

    // 原有的Provider判断逻辑...
}

// 新增位置有效性校验方法,可根据需求调整有效期
private static bool IsLocationValid(Location location)
{
    // 判断位置是否在5分钟内生成
    return (DateTime.UtcNow.Ticks - location.Time) < (5 * 60 * 10000000);
}

2. 改用单次位置更新请求

原代码使用的RequestLocationUpdates设置了30秒间隔,会导致系统延迟推送位置。改为请求单次即时更新:

// 替换原RequestLocationUpdates调用
locationManager.RequestSingleUpdate(provider, _this, Looper.MainLooper);

单次更新会让系统优先返回当前可用位置,而非等待满足间隔/距离条件。

3. 同时监听多个位置源

不要仅依赖单个Provider,同时请求Network和GPS,哪个先返回就用哪个:

private async static Task<(double lat, double lon)?> GetLocation()
{
    LocationManager locationManager = (LocationManager)_this.GetSystemService(LocationService);
    var tcs = new TaskCompletionSource<Location>();
    var callback = new LocationCallback(tcs);

    // 同时注册多个可用的位置源
    if (locationManager.IsProviderEnabled(LocationManager.NetworkProvider))
    {
        locationManager.RequestSingleUpdate(LocationManager.NetworkProvider, callback, Looper.MainLooper);
    }
    if (locationManager.IsProviderEnabled(LocationManager.GpsProvider))
    {
        locationManager.RequestSingleUpdate(LocationManager.GpsProvider, callback, Looper.MainLooper);
    }

    // 等待10秒超时
    var completedTask = await Task.WhenAny(tcs.Task, Task.Delay(10000));
    if (completedTask == tcs.Task && tcs.Task.Result != null)
    {
        return (tcs.Task.Result.Latitude, tcs.Task.Result.Longitude);
    }
    else
    {
        // 超时后尝试返回缓存位置
        var cached = locationManager.GetLastKnownLocation(LocationManager.NetworkProvider) 
                    ?? locationManager.GetLastKnownLocation(LocationManager.GpsProvider);
        return cached != null && IsLocationValid(cached) ? (cached.Latitude, cached.Longitude) : null;
    }
}

// 自定义位置回调类
private class LocationCallback : ILocationListener
{
    private TaskCompletionSource<Location> _tcs;
    public LocationCallback(TaskCompletionSource<Location> tcs) => _tcs = tcs;

    public void OnLocationChanged(Location location)
    {
        if (!_tcs.Task.IsCompleted) _tcs.SetResult(location);
    }

    // 实现其他接口方法(空实现即可)
    public void OnProviderDisabled(string provider) {}
    public void OnProviderEnabled(string provider) {}
    public void OnStatusChanged(string provider, Availability status, Bundle extras) {}
}

4. 转移逻辑到后台服务

BroadcastReceiver的OnReceive运行在主线程,长时间异步等待可能触发ANR,且系统对后台操作有严格限制。建议在OnReceive中启动ForegroundService,将位置获取逻辑移到服务中执行,避免被系统限制。

5. 确保后台位置权限

Android 10+需要申请ACCESS_BACKGROUND_LOCATION权限,且用户需允许应用在后台获取位置。检查权限配置,确保蓝牙断开时应用具备位置获取权限。

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 14:31:23