Xamarin.Android:如何从EventHandler传值到后台服务并监听BLE连接状态
我来帮你一步步搞定这个需求,分三个核心部分来实现:改造GattCallback触发状态事件、完善后台服务监听事件并发送通知、实现事件向服务传值的逻辑。
咱们先给你的GattCallback类加一个EventHandler,这样当设备连接/断开时,能把状态信息抛出去。修改后的代码如下:
public class CustomGattCallback : BluetoothGattCallback { // 定义事件,用来传递连接状态信息 public event EventHandler<ConnectionStateChangedEventArgs> ConnectionStateChanged; public override void OnConnectionStateChange(BluetoothGatt gatt, [GeneratedEnum] GattStatus status, [GeneratedEnum] ProfileState newState) { base.OnConnectionStateChange(gatt, status, newState); // 触发事件,把状态信息传出去 ConnectionStateChanged?.Invoke(this, new ConnectionStateChangedEventArgs { Gatt = gatt, Status = status, NewState = newState }); } } // 自定义事件参数类,用来携带状态数据 public class ConnectionStateChangedEventArgs : EventArgs { public BluetoothGatt Gatt { get; set; } public GattStatus Status { get; set; } public ProfileState NewState { get; set; } }
简单说下:这里创建了一个自定义的事件参数类,把Gatt对象、状态码和新状态都封装进去,然后在OnConnectionStateChange里触发事件,这样任何订阅这个事件的对象(比如你的后台服务)都能收到状态变化通知。
接下来要修改你的后台服务,让它能订阅上面的事件,并且在收到状态变化时发送本地通知。还要注意Android 8.0+必须创建通知渠道,不然通知弹不出来。修改后的服务代码:
[Service] public class DemoService : Service { private CustomGattCallback _gattCallback; private NotificationManager _notificationManager; private const string ChannelId = "BLE_Connection_Channel"; private const int NotificationId = 1001; public override void OnCreate() { base.OnCreate(); // 初始化通知管理器和渠道 _notificationManager = (NotificationManager)GetSystemService(NotificationService); CreateNotificationChannel(); // 初始化GattCallback并订阅事件 _gattCallback = new CustomGattCallback(); _gattCallback.ConnectionStateChanged += OnGattConnectionStateChanged; } public override StartCommandResult OnStartCommand(Android.Content.Intent intent, StartCommandFlags flags, int startId) { // 这里不要马上StopSelf,保持服务运行来监听BLE状态 Log.Debug("DemoService", "Service started, listening for BLE connection changes"); return StartCommandResult.Sticky; } private void OnGattConnectionStateChanged(object sender, ConnectionStateChangedEventArgs e) { string notificationTitle; string notificationContent; // 根据新状态判断是连接还是断开 if (e.NewState == ProfileState.Connected) { notificationTitle = "BLE设备已连接"; notificationContent = $"设备: {e.Gatt.Device.Name}"; } else if (e.NewState == ProfileState.Disconnected) { notificationTitle = "BLE设备已断开"; notificationContent = $"设备: {e.Gatt.Device.Name}"; } else { return; // 其他状态忽略 } // 发送本地通知 SendNotification(notificationTitle, notificationContent); } private void CreateNotificationChannel() { if (Build.VERSION.SdkInt >= BuildVersionCodes.O) { var channel = new NotificationChannel(ChannelId, "BLE连接状态通知", NotificationImportance.Default) { Description = "用于展示BLE设备的连接/断开状态" }; _notificationManager.CreateNotificationChannel(channel); } } private void SendNotification(string title, string content) { var notificationBuilder = new NotificationCompat.Builder(this, ChannelId) .SetContentTitle(title) .SetContentText(content) .SetSmallIcon(Android.Resource.Drawable.IcDialogInfo) .SetAutoCancel(true); var notification = notificationBuilder.Build(); _notificationManager.Notify(NotificationId, notification); } public override void OnDestroy() { // 取消事件订阅,避免内存泄漏 _gattCallback.ConnectionStateChanged -= OnGattConnectionStateChanged; base.OnDestroy(); } public override IBinder OnBind(Intent intent) { return null; // 如果不需要绑定服务,返回null即可 } }
重点说明:
- 在
OnCreate里初始化了GattCallback并订阅了它的ConnectionStateChanged事件,服务一创建就能监听状态变化 - 实现了通知渠道的创建,适配Android 8.0+的系统要求
OnGattConnectionStateChanged方法里根据状态生成不同的通知内容,然后调用SendNotification发送OnStartCommand里去掉了StopSelf,因为要保持服务运行来监听BLE状态,返回Sticky让系统在内存不足杀死服务后自动重启它
其实上面的代码已经实现了传值功能:CustomGattCallback的ConnectionStateChanged事件通过ConnectionStateChangedEventArgs把Gatt对象、状态码和新状态都传递给了DemoService的OnGattConnectionStateChanged方法。如果需要传递更多自定义数据,只需要修改ConnectionStateChangedEventArgs类,添加你需要的属性即可。
举个例子,如果要传递设备MAC地址,只需要修改事件参数类:
public class ConnectionStateChangedEventArgs : EventArgs { public BluetoothGatt Gatt { get; set; } public GattStatus Status { get; set; } public ProfileState NewState { get; set; } public string DeviceMacAddress { get; set; } // 新增的属性 }
然后在GattCallback的OnConnectionStateChange里赋值:
ConnectionStateChanged?.Invoke(this, new ConnectionStateChangedEventArgs { Gatt = gatt, Status = status, NewState = newState, DeviceMacAddress = gatt.Device.Address // 赋值MAC地址 });
这样服务就能在OnGattConnectionStateChanged里拿到DeviceMacAddress并使用了。
内容的提问来源于stack exchange,提问作者crazydev

