You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

在Xamarin Droid中设置BT为可发现模式并接受SPP连接

嘿,刚上手Xamarin开发蓝牙功能确实容易摸不着头绪,我来给你拆解一下开启可发现模式和接收外部主机连接的具体实现步骤,分Android和iOS两端来说,毕竟俩平台的蓝牙逻辑差异还挺大的~

实现Xamarin蓝牙可发现模式与接收连接的步骤

1. 先搞定蓝牙权限配置

不管哪端,权限都是第一步,蓝牙相关操作必须先拿到系统授权:

Android端

AndroidManifest.xml里添加必要权限,Android 12+还需要新增几个细化权限:

<!-- 基础蓝牙权限 -->
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
<!-- Android 6+ 扫描蓝牙需要位置权限 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- Android 12+ 新增权限 -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<!-- 声明蓝牙功能 -->
<uses-feature android:name="android.hardware.bluetooth" android:required="true" />

注意:Android 12+的BLUETOOTH_SCANBLUETOOTH_CONNECT需要动态申请,不能只靠Manifest,得在代码里调用ActivityCompat.RequestPermissions请求用户授权。

iOS端

Info.plist里添加蓝牙权限描述,否则App会被拒:

<key>NSBluetoothAlwaysUsageDescription</key>
<string>需要蓝牙权限来接收外部设备的连接</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>需要蓝牙权限来与外部设备交互</string>
<!-- 如果需要后台蓝牙广播,还要加后台模式权限 -->
<key>UIBackgroundModes</key>
<array>
    <string>bluetooth-peripheral</string>
</array>

2. 开启可发现模式

Android端:直接调用系统Intent

Android有现成的系统Intent可以请求开启可发现模式,还能设置可发现时长(最多300秒):

// 先获取蓝牙适配器
var bluetoothAdapter = BluetoothAdapter.DefaultAdapter;
if (bluetoothAdapter == null || !bluetoothAdapter.IsEnabled)
{
    // 蓝牙未开启,先引导用户开启
    var enableBtIntent = new Intent(BluetoothAdapter.ActionRequestEnable);
    StartActivityForResult(enableBtIntent, 0);
    return;
}

// 请求开启可发现模式
var discoverableIntent = new Intent(BluetoothAdapter.ActionRequestDiscoverable);
discoverableIntent.PutExtra(BluetoothAdapter.ExtraDiscoverableDuration, 300); // 300秒内可被发现
StartActivityForResult(discoverableIntent, 1);

之后可以在OnActivityResult里判断用户是否允许了可发现模式:

protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
    base.OnActivityResult(requestCode, resultCode, data);
    if (requestCode == 1)
    {
        if (resultCode == Result.Ok)
        {
            // 可发现模式已开启
        }
        else
        {
            // 用户拒绝了开启请求
        }
    }
}

iOS端:通过广播服务让外部发现

iOS没有“可发现模式”的直接概念,而是通过CoreBluetooth框架的外设模式,广播自定义服务来让外部设备扫描到:

using CoreBluetooth;

CBPeripheralManager _peripheralManager;
CBMutableService _customService;
CBUUID _serviceUuid = CBUUID.FromString("YOUR_UNIQUE_SERVICE_UUID"); // 自定义一个UUID,外部设备要用这个来连接

public void SetupBluetoothAdvertising()
{
    _peripheralManager = new CBPeripheralManager(null, null);
    _peripheralManager.StateUpdated += PeripheralManager_StateUpdated;
}

private void PeripheralManager_StateUpdated(object sender, EventArgs e)
{
    if (_peripheralManager.State == CBPeripheralManagerState.PoweredOn)
    {
        // 创建自定义服务和特征
        _customService = new CBMutableService(_serviceUuid, true);
        
        var characteristicUuid = CBUUID.FromString("YOUR_CHARACTERISTIC_UUID");
        var characteristic = new CBMutableCharacteristic(characteristicUuid,
            CBCharacteristicProperties.Read | CBCharacteristicProperties.Write,
            null,
            CBAttributePermissions.Readable | CBAttributePermissions.Writeable);
        
        _customService.Characteristics = new CBMutableCharacteristic[] { characteristic };
        _peripheralManager.AddService(_customService);
        
        // 开始广播设备名称和服务UUID
        var advertisementData = new CBAdvertisementData
        {
            LocalName = "MyXamarinDevice", // 外部设备看到的设备名称
            ServiceUUIDs = new CBUUID[] { _serviceUuid }
        };
        _peripheralManager.StartAdvertising(advertisementData);
    }
}

说明:外部中心设备(比如你的主机)需要扫描这个自定义的服务UUID,才能找到你的iOS设备并发起连接。

3. 接收外部主机的连接请求

Android端:用BluetoothServerSocket监听

Android通过创建服务器套接字来监听外部连接,需要在后台线程执行,避免阻塞UI:

BluetoothServerSocket _serverSocket;

public void StartBluetoothServer()
{
    var bluetoothAdapter = BluetoothAdapter.DefaultAdapter;
    if (bluetoothAdapter == null || !bluetoothAdapter.IsEnabled)
    {
        return;
    }
    
    // 用之前自定义的UUID,外部设备必须用相同的UUID才能连接
    var uuid = UUID.FromString("YOUR_UNIQUE_SERVICE_UUID");
    _serverSocket = bluetoothAdapter.ListenUsingRfcommWithServiceRecord("MyBluetoothService", uuid);
    
    // 开启后台线程监听连接
    new Thread(() =>
    {
        try
        {
            // 阻塞等待外部连接
            BluetoothSocket clientSocket = _serverSocket.Accept();
            
            // 连接成功!现在可以处理数据交互
            Stream inputStream = clientSocket.InputStream;
            Stream outputStream = clientSocket.OutputStream;
            
            // 示例:读取外部发送的数据
            byte[] buffer = new byte[1024];
            int bytesRead = inputStream.Read(buffer, 0, buffer.Length);
            string receivedText = Encoding.UTF8.GetString(buffer, 0, bytesRead);
            
            // 后续可以根据业务逻辑处理数据
        }
        catch (IOException ex)
        {
            // 处理连接异常
        }
        finally
        {
            try
            {
                _serverSocket?.Close();
            }
            catch (IOException ex)
            {
                // 关闭套接字异常处理
            }
        }
    }).Start();
}

iOS端:处理连接与数据交互

iOS作为外设,当外部中心设备连接过来时,会触发相关回调,我们需要处理读取/写入请求:

// 在初始化外设管理器时添加回调
_peripheralManager.DidReceiveReadRequest += PeripheralManager_DidReceiveReadRequest;
_peripheralManager.DidReceiveWriteRequests += PeripheralManager_DidReceiveWriteRequests;

private void PeripheralManager_DidReceiveReadRequest(object sender, CBPeripheralManagerDidReceiveReadRequestEventArgs e)
{
    var request = e.Request;
    if (request.Characteristic.UUID.Equals(_characteristicUuid))
    {
        // 返回数据给外部设备
        var responseData = NSData.FromString("Hello from Xamarin iOS");
        _peripheralManager.RespondToRequest(request, CBATTError.Success, responseData);
    }
    else
    {
        _peripheralManager.RespondToRequest(request, CBATTError.AttributeNotFound, null);
    }
}

private void PeripheralManager_DidReceiveWriteRequests(object sender, CBPeripheralManagerDidReceiveWriteRequestsEventArgs e)
{
    foreach (var request in e.Requests)
    {
        if (request.Characteristic.UUID.Equals(_characteristicUuid))
        {
            // 读取外部设备发送的数据
            string receivedData = request.Value.ToString();
            // 处理数据逻辑
        }
    }
    // 响应写入请求
    _peripheralManager.RespondToRequest(e.Requests[0], CBATTError.Success);
}

几个关键提醒

  • 所有蓝牙操作都要先检查蓝牙是否处于开启状态,未开启时要引导用户开启(Android可以直接发请求,iOS需要用户手动去设置里打开)。
  • Android的蓝牙服务器监听一定要放在后台线程,否则会阻塞UI线程导致ANR。
  • iOS的蓝牙外设模式依赖BLE(蓝牙低功耗),确保测试设备支持BLE功能。

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

火山引擎 最新活动