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

如何实现可与BLE客户端通信的Xamarin.Forms BLE服务端应用

Xamarin.Forms 实现BLE服务端方案

Xamarin.Forms 本身没有内置BLE GATT服务端(外设角色)的跨平台抽象API,需要通过依赖服务(DependencyService) 分别对接Android、iOS原生平台的BLE外设能力,封装统一接口给共享项目调用即可,不需要引入第三方服务。


核心实现逻辑

  • 自定义固定UUID的GATT主服务,下挂1个同时支持「写入」「通知」属性的特征:客户端通过写入特征发送字符串消息,服务端收到消息处理后,通过特征的通知通道向已连接客户端返回字符串响应。
  • 近场通信不需要额外开发检测逻辑:只要服务端开启可连接广播,客户端在蓝牙信号覆盖范围内扫描到对应服务、完成GATT连接和服务发现后,即可主动向服务端发消息,完全匹配需求。
  • 提前处理平台权限:Android 6.0+需动态申请定位权限,Android 12+需额外申请BLUETOOTH_ADVERTISE/BLUETOOTH_CONNECT/BLUETOOTH_SCAN权限;iOS需在Info.plist中添加NSBluetoothAlwaysUsageDescription、NSBluetoothPeripheralUsageDescription权限描述,且前台状态下才能正常广播。

具体实现步骤

1. 共享层定义跨平台接口

在Xamarin.Forms共享项目中定义统一的BLE服务端接口,供各原生平台实现:

public interface IBleServer
{
    // 初始化GATT服务、开启广播
    Task StartServerAsync();
    // 停止广播、释放服务资源
    Task StopServerAsync();
    // 接收到客户端消息的触发事件
    event EventHandler<string> MessageReceived;
    // 向所有已连接客户端发送响应字符串
    Task SendResponseAsync(string response);
}

2. Android平台原生实现

通过Android原生BluetoothGattServer、BluetoothLeAdvertiserAPI实现逻辑,注册为依赖服务:

[assembly: Dependency(typeof(BleServerImpl))]
namespace YourApp.Droid
{
    public class BleServerImpl : IBleServer
    {
        private BluetoothGattServer _gattServer;
        private BluetoothGattCharacteristic _dataCharacteristic;
        private readonly List<BluetoothDevice> _connectedDevices = new List<BluetoothDevice>();
        // 自定义UUID,客户端需保持完全一致
        private readonly Java.Util.UUID _serviceUuid = Java.Util.UUID.FromString("0000ffe0-0000-1000-8000-00805f9b34fb");
        private readonly Java.Util.UUID _charUuid = Java.Util.UUID.FromString("0000ffe1-0000-1000-8000-00805f9b34fb");

        public event EventHandler<string> MessageReceived;

        public async Task StartServerAsync()
        {
            var bluetoothManager = (BluetoothManager)Android.App.Application.Context.GetSystemService(Context.BluetoothService);
            _gattServer = bluetoothManager.OpenGattServer(Android.App.Application.Context, new ServerCallback(this));
            
            // 构建GATT服务和特征
            var service = new BluetoothGattService(_serviceUuid, GattServiceType.Primary);
            _dataCharacteristic = new BluetoothGattCharacteristic(
                _charUuid,
                GattProperty.Write | GattProperty.Notify,
                GattPermission.Write
            );
            service.AddCharacteristic(_dataCharacteristic);
            _gattServer.AddService(service);

            // 开启BLE广播
            var advertiser = BluetoothAdapter.DefaultAdapter.BluetoothLeAdvertiser;
            var adSettings = new AdvertiseSettings.Builder()
                .SetAdvertiseMode(AdvertiseMode.LowLatency)
                .SetConnectable(true)
                .SetTxPowerLevel(AdvertiseTxPower.High)
                .Build();
            var adData = new AdvertiseData.Builder()
                .SetIncludeDeviceName(true)
                .AddServiceUuid(new ParcelUuid(_serviceUuid))
                .Build();
            advertiser.StartAdvertising(adSettings, adData, new SimpleAdCallback());
        }

        public Task SendResponseAsync(string response)
        {
            byte[] respData = System.Text.Encoding.UTF8.GetBytes(response);
            _dataCharacteristic.SetValue(respData);
            // 向所有已连接客户端推送通知
            foreach (var device in _connectedDevices)
            {
                _gattServer.NotifyCharacteristicChanged(device, _dataCharacteristic, false);
            }
            return Task.CompletedTask;
        }

        public Task StopServerAsync()
        {
            BluetoothAdapter.DefaultAdapter.BluetoothLeAdvertiser?.StopAdvertising(new SimpleAdCallback());
            _gattServer?.Close();
            _gattServer = null;
            _connectedDevices.Clear();
            return Task.CompletedTask;
        }

        // GATT服务端回调处理
        private class ServerCallback : BluetoothGattServerCallback
        {
            private readonly BleServerImpl _parent;
            public ServerCallback(BleServerImpl parent) => _parent = parent;

            public override void OnConnectionStateChange(BluetoothDevice device, ProfileState status, ProfileState newState)
            {
                base.OnConnectionStateChange(device, status, newState);
                if (newState == ProfileState.Connected) _parent._connectedDevices.Add(device);
                if (newState == ProfileState.Disconnected) _parent._connectedDevices.Remove(device);
            }

            public override void OnCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, bool preparedWrite, bool responseNeeded, int offset, byte[] value)
            {
                base.OnCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value);
                // 解析客户端发送的字符串
                string receivedMsg = System.Text.Encoding.UTF8.GetString(value);
                _parent.MessageReceived?.Invoke(_parent, receivedMsg);
                // 返回写入成功响应
                if (responseNeeded) _parent._gattServer.SendResponse(device, requestId, GattStatus.Success, offset, value);
            }
        }

        private class SimpleAdCallback : AdvertiseCallback { }
    }
}

3. iOS平台原生实现

通过iOS原生CBPeripheralManagerAPI实现外设逻辑,注册为依赖服务:

[assembly: Dependency(typeof(BleServerImpl))]
namespace YourApp.iOS
{
    public class BleServerImpl : IBleServer
    {
        private CBPeripheralManager _peripheralManager;
        private CBMutableCharacteristic _dataCharacteristic;
        private readonly List<CBCentral> _connectedCentrals = new List<CBCentral>();
        private readonly CBUUID _serviceUuid = CBUUID.FromString("0000ffe0-0000-1000-8000-00805f9b34fb");
        private readonly CBUUID _charUuid = CBUUID.FromString("0000ffe1-0000-1000-8000-00805f9b34fb");
        public event EventHandler<string> MessageReceived;

        public Task StartServerAsync()
        {
            _peripheralManager = new CBPeripheralManager(new PeripheralDelegate(this), DispatchQueue.MainQueue);
            return Task.CompletedTask;
        }

        public Task SendResponseAsync(string response)
        {
            byte[] respData = System.Text.Encoding.UTF8.GetBytes(response);
            _peripheralManager.UpdateValue(NSData.FromArray(respData), _dataCharacteristic, _connectedCentrals.ToArray());
            return Task.CompletedTask;
        }

        public Task StopServerAsync()
        {
            _peripheralManager.StopAdvertising();
            _peripheralManager.RemoveAllServices();
            _connectedCentrals.Clear();
            return Task.CompletedTask;
        }

        private class PeripheralDelegate : CBPeripheralManagerDelegate
        {
            private readonly BleServerImpl _parent;
            public PeripheralDelegate(BleServerImpl parent) => _parent = parent;

            public override void StateUpdated(CBPeripheralManager peripheral)
            {
                if (peripheral.State != CBPeripheralManagerState.PoweredOn) return;
                // 蓝牙可用时初始化服务和特征
                var service = new CBMutableService(_parent._serviceUuid, true);
                _parent._dataCharacteristic = new CBMutableCharacteristic(
                    _parent._charUuid,
                    CBCharacteristicProperties.Write | CBCharacteristicProperties.Notify,
                    CBAttributePermissions.Writeable | CBAttributePermissions.Readable,
                    null
                );
                service.Characteristics = new[] { _parent._dataCharacteristic };
                peripheral.AddService(service);
                // 开启广播
                peripheral.StartAdvertising(new NSDictionary(
                    CBAdvertisement.DataServiceUUIDsKey, NSArray.FromObjects(_parent._serviceUuid),
                    CBAdvertisement.DataLocalNameKey, "BLE_Server"
                ));
            }

            public override void DidSubscribeToCharacteristic(CBPeripheralManager peripheral, CBCentral central, CBCharacteristic characteristic)
            {
                _parent._connectedCentrals.Add(central);
            }

            public override void DidUnsubscribeFromCharacteristic(CBPeripheralManager peripheral, CBCentral central, CBCharacteristic characteristic)
            {
                _parent._connectedCentrals.Remove(central);
            }

            public override void WriteRequestsReceived(CBPeripheralManager peripheral, CBATTRequest[] requests)
            {
                foreach (var req in requests)
                {
                    if (req.Characteristic.UUID.Equals(_parent._charUuid))
                    {
                        string receivedMsg = System.Text.Encoding.UTF8.GetString(req.Value.ToArray());
                        _parent.MessageReceived?.Invoke(_parent, receivedMsg);
                        peripheral.RespondToRequest(req, CBATTError.Success);
                    }
                }
            }
        }
    }
}

4. 共享层调用示例

在Xamarin.Forms页面/业务逻辑中直接通过依赖服务调用即可:

var bleServer = DependencyService.Get<IBleServer>();
// 监听收到的客户端消息
bleServer.MessageReceived += async (s, msg) =>
{
    // 这里写自定义业务逻辑
    Console.WriteLine($"收到客户端消息:{msg}");
    // 返回响应
    await bleServer.SendResponseAsync($"服务端已接收消息,响应时间:{DateTime.Now:T}");
};
// 启动服务
await bleServer.StartServerAsync();

常见踩坑说明

  • 服务端、客户端的服务UUID、特征UUID必须完全一致,否则客户端无法找到对应通信特征。
  • 字符串编解码两端统一用UTF8,避免中文乱码。
  • BLE单包广播载荷最大31字节,不要在广播包中塞入过多自定义数据,否则会导致广播失败。
  • 客户端连接服务端后,必须先开启对应特征的通知订阅,否则无法收到服务端返回的响应消息。
  • iOS后台状态下BLE广播会被系统降频、限制,建议服务端应用保持前台使用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 00:31:08