蓝牙断开时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
相关产品推荐
相关产品推荐

