Xamarin.Forms iOS端锁屏后台BLE扫描问题求助
Xamarin.Forms iOS 后台BLE扫描问题解决指南
问题场景
我在Xamarin.Forms iOS应用中尝试实现锁屏状态下的后台BLE设备扫描,但遇到了几个关键问题。
环境信息
- 使用Xamarin & MAUI的BLE插件执行扫描操作;
- 通过Xamarin.Forms DependencyService从共享代码调用原生平台功能(启动/停止扫描);
- 已在Info.plist中启用后台模式;
- 测试设备:iPhone 8(iOS 15.6.1)、iPad Air 3(iOS 15.1)。
遇到的问题
- 屏幕锁屏后扫描直接停止;
- 后台运行约30秒后进程停止,应用被系统杀死;
- 基于服务GUID的扫描过滤完全失效。
当前配置与代码
Info.plist 配置
<key>UIBackgroundModes</key> <array> <string>location</string> <string>external-accessory</string> <string>bluetooth-central</string> <string>bluetooth-peripheral</string> <string>fetch</string> <string>processing</string> </array> <key>NSBluetoothPeripheralUsageDescription</key> <string>YOUR CUSTOM MESSAGE</string> <key>NSBluetoothAlwaysUsageDescription</key> <string>YOUR CUSTOM MESSAGE</string> <key>NSLocationAlwaysAndWhenInUseUsageDescription</key> <string>YOUR CUSTOM MESSAGE</string> <key>NSLocationAlwaysUsageDescription</key> <string>YOUR CUSTOM MESSAGE</string> <key>NSLocationWhenInUseUsageDescription</key> <string>Your message</string>
iOS原生扫描代码(BLE_Scanner_iOS)
using System; using Xamarin.Forms; using Scan_BLE_test.iOS; using Plugin.BLE; using Plugin.BLE.Abstractions.Contracts; using System.Threading.Tasks; using UIKit; using Plugin.BLE.Abstractions; [assembly: Dependency(typeof(BLE_Scanner_iOS))] namespace Scan_BLE_test.iOS { public class BLE_Scanner_iOS : IStartStopScan { private static readonly object syncObj = new object(); // 线程安全锁 /* 日志记录用数据 */ private static int current_rssi = 0; private static string selected_device_name = ""; IAdapter Adapter; IBluetoothLE Ble; bool runningFlag; public void Start_Ble_Scan() { runningFlag = true; Ble = CrossBluetoothLE.Current; Adapter = CrossBluetoothLE.Current.Adapter; Task.Factory.StartNew(async () => { nint taskId = 0; // 后台时间耗尽时触发 taskId = UIApplication.SharedApplication.BeginBackgroundTask(() => { Console.WriteLine("后台时间耗尽"); UIApplication.SharedApplication.EndBackgroundTask(taskId); }); while (runningFlag) { lock (syncObj) { Console.WriteLine("运行循环中..."); Console.WriteLine(UIApplication.SharedApplication.BackgroundTimeRemaining); Scan_BLE(10000); Console.WriteLine($"设备 {selected_device_name} RSSI: {current_rssi}"); } await Task.Delay(1000); } UIApplication.SharedApplication.EndBackgroundTask(taskId); }); } async void Scan_BLE(int scan_time_i) { if (Ble.State == BluetoothState.On) { if (!Adapter.IsScanning) { Adapter.ScanTimeout = scan_time_i; Adapter.ScanMode = ScanMode.Balanced; // 注册设备发现事件 Adapter.DeviceDiscovered += (s, a) => { if (!string.IsNullOrEmpty(a.Device.Name)) { lock (syncObj) { current_rssi = a.Device.Rssi; selected_device_name = a.Device.Name; } } }; var scanFilterOptions = new ScanFilterOptions(); // 这里的GUID过滤不生效 //scanFilterOptions.ServiceUuids = new[] { // Guid.Parse("F640A514-908B-CCAF-E5EB-89AE701CF346") //}; await Adapter.StartScanningForDevicesAsync(scanFilterOptions); System.Diagnostics.Debug.WriteLine("[INFO] 扫描完成"); } else { System.Diagnostics.Debug.WriteLine("[WARNING] 设备正在扫描"); } } else { System.Diagnostics.Debug.WriteLine("[WARNING] BLE未开启"); } } public void Stop_Ble_Scan() { runningFlag = false; } } }
输出日志
锁屏前能正常获取Beacon数据,之后日志停止,应用被系统关闭:
[0:] [WARNING] device is scaning 2022-11-16 12:49:49.063 Xamarin.PreBuilt.iOS[46180:1980782] Running loop... 2022-11-16 12:49:49.064 Xamarin.PreBuilt.iOS[46180:1980782] 26,5684510833526 2022-11-16 12:49:49.066 Xamarin.PreBuilt.iOS[46180:1980782] Device Beacon1 rssi -85 2022-11-16 12:49:50.091 Xamarin.PreBuilt.iOS[46180:1980781] Running loop... [0:] [WARNING] device is scaning 2022-11-16 12:49:50.092 Xamarin.PreBuilt.iOS[46180:1980781] 25,540871916688 2022-11-16 12:49:50.094 Xamarin.PreBuilt.iOS[46180:1980781] Device Beacon1 rssi -85 [0:] [WARNING] device is scaning 2022-11-16 12:49:51.134 Xamarin.PreBuilt.iOS[46180:1980781] Running loop... 2022-11-16 12:49:51.135 Xamarin.PreBuilt.iOS[46180:1980781] 24,4981506250042 2022-11-16 12:49:51.136 Xamarin.PreBuilt.iOS[46180:1980781] Device Beacon1 rssi -85
问题解决步骤
1. 后台扫描被终止的问题
iOS对后台BLE扫描有严格限制,你的代码逻辑和配置存在以下问题:
- 冗余的后台模式:
external-accessory、fetch、processing这些权限和BLE后台扫描无关,反而会让系统认为你在滥用后台权限,直接干掉应用。只保留bluetooth-central(如果需要结合位置增强稳定性可以保留location); - 错误的扫描循环:你用10秒超时扫描+1秒延迟循环的方式,每次扫描结束后重启,这种方式在后台会被系统判定为无效后台活动,很快被终止。应该改用持续扫描,只在扫描意外停止时重启;
- 后台任务管理不当:不要在循环里反复创建后台任务,应该在扫描启动时创建一个后台任务,直到扫描停止再结束它。
修改后的扫描启动逻辑:
public void Start_Ble_Scan() { runningFlag = true; Ble = CrossBluetoothLE.Current; Adapter = CrossBluetoothLE.Current.Adapter; // 避免重复注册事件 Adapter.DeviceDiscovered -= HandleDeviceDiscovered; Adapter.DeviceDiscovered += HandleDeviceDiscovered; // 创建单个后台任务 var taskId = UIApplication.SharedApplication.BeginBackgroundTask(() => { Console.WriteLine("后台时间耗尽,停止扫描"); Stop_Ble_Scan(); UIApplication.SharedApplication.EndBackgroundTask(taskId); }); Task.Run(async () => { while (runningFlag) { if (Ble.State == BluetoothState.On && !Adapter.IsScanning) { // 后台用低功耗扫描模式更稳定 Adapter.ScanMode = ScanMode.LowPower; // 无超时持续扫描 await Adapter.StartScanningForDevicesAsync(new ScanFilterOptions()); } // 5秒检查一次扫描状态 await Task.Delay(5000); } UIApplication.SharedApplication.EndBackgroundTask(taskId); }); } private void HandleDeviceDiscovered(object sender, Plugin.BLE.Abstractions.EventArgs.DeviceEventArgs e) { if (!string.IsNullOrEmpty(e.Device.Name)) { lock (syncObj) { current_rssi = e.Device.Rssi; selected_device_name = e.Device.Name; Console.WriteLine($"设备 {selected_device_name} RSSI: {current_rssi}"); } } }
2. GUID过滤失效的问题
iOS的BLE扫描过滤只对广播包中包含服务UUID的设备生效。如果你的BLE设备没有在广播数据里主动发送这个UUID,过滤规则就会失效。解决办法:
- 确认BLE设备的固件配置,确保它在广播包中包含目标服务UUID;
- 如果设备不支持广播UUID,只能在
HandleDeviceDiscovered事件中手动过滤:
private void HandleDeviceDiscovered(object sender, Plugin.BLE.Abstractions.EventArgs.DeviceEventArgs e) { var targetServiceId = Guid.Parse("F640A514-908B-CCAF-E5EB-89AE701CF346"); // 检查设备是否包含目标服务 if (e.Device.Services?.Any(s => s.Id == targetServiceId) == true) { lock (syncObj) { current_rssi = e.Device.Rssi; selected_device_name = e.Device.Name; Console.WriteLine($"匹配目标设备 {selected_device_name} RSSI: {current_rssi}"); } } }
3. 锁屏后扫描停止的问题
- 确保用户授予了始终允许的蓝牙权限(对应
NSBluetoothAlwaysUsageDescription),仅“使用期间允许”的权限在锁屏后会被限制; - 如果需要更稳定的后台扫描,可以结合
location后台模式(同时申请位置权限),iOS对位置相关的后台任务优先级更高; - 后台尽量减少CPU消耗,避免不必要的日志输出和计算,降低被系统杀死的概率。
内容的提问来源于stack exchange,提问作者Jakub
相关产品推荐
相关产品推荐

