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

Android Java/Xamarin 连接已发现蓝牙设备无法触发配对请求排查

问题背景

在Xamarin绑定封装的Android Java项目中,已实现范围内蓝牙设备扫描功能,扫描流程正常,可将搜索到的可发现未知蓝牙设备名称填充至ListView控件。根据Android官方蓝牙开发文档说明,无需提前判断设备是否已配对,调用BluetoothSocket.Connect方法时Android系统会自动发起配对流程。由于BluetoothSocket.Connect()在主线程运行会占用大量计算资源导致应用卡顿,因此继承Thread类将蓝牙连接逻辑写入该类的Run方法中执行。

异常日志

点击ListView中设备名称时,初始Logcat输出如下日志:

BluetoothClient D/BluetoothUtils: isSocketAllowedBySecurityPolicy start : device null
BluetoothClient I/BluetoothSocket: connect() for device 74C17D called by pid: 23141
BluetoothClient W/BluetoothAdapter: getBluetoothService() called with no BluetoothManagerCallback

排查后确认原因为使用的UUID错误,改为读取设备自身支持的UUID列表后,原有报错消除,Logcat仅输出如下日志:

BluetoothClient.BluetoothClient I/BluetoothAdapter: cancelDiscovery
BluetoothClient.BluetoothClient I/BluetoothAdapter: cancelDiscovery = false
问题复现代码

当前使用的蓝牙连接实现代码如下:

[Activity(Label = "@string/app_name", Theme = "@style/AppTheme", MainLauncher = true)]
public class MainActivity : AppCompatActivity
{
  protected override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState); 
        // 存储扫描到的蓝牙设备友好名称的ListView
        _deviceList = FindViewById<ListView>(Resource.Id.mylist);
         // 处理蓝牙设备名称点击事件
        _deviceList.ItemClick += (o, e) =>
        {
            // 初始化蓝牙设备对象为空
            BluetoothDevice _mydevice=null;
            // 读取点击项对应的设备名称
            string name = (string)_deviceList.GetItemAtPosition(e.Position);
            // 遍历所有已发现蓝牙设备匹配名称
            foreach (var device in _bluetoothDevices)
            {
                if (device.Name == name)
                {
                    // 匹配到目标设备后终止循环
                    _mydevice = device;
                    Log.Info("MainActivity", device.Name);
                    break;
                }
            }

            if (_mydevice != null)
            {
                try
                {
                    BluetoothThread thread = new BluetoothThread(_mydevice,this);
                    thread.Run();
                }
                catch (Exception m)
                {
                    Toast.MakeText(this,m.Message,ToastLength.Short).Show();
                }
            }
            
        };
    }
}
// 蓝牙连接多线程处理类
public class BluetoothThread : Thread
{
    private BluetoothDevice dev;
    private Context ctx;

    public BluetoothThread()
    {
        
    }
    
    public BluetoothThread(BluetoothDevice device,Context context)
    {
        dev = device;
        ctx = context;
    }
    public override void Run()
    {
        try
        {
            Task.Run(() =>
            {
                   ParcelUuid[] parcelUuids = dev.GetUuids();
                   BluetoothSocket socket = dev.CreateRfcommSocketToServiceRecord(UUID.FromString(parcelUuids[0].ToString()));
                socket.Connect();
            });
           
        }
        catch (Exception p)
        {
         Toast.MakeText(ctx,p.Message,ToastLength.Short).Show();
        }
    }
}

预期调用socket.Connect()时应用会向远端设备发起配对请求,但实际无任何配对弹窗出现,仅输出上述日志信息。

问题根因
  • 线程启动逻辑错误:直接调用thread.Run()是在当前主线程同步执行方法体,并未真正启动独立工作线程。后续嵌套的Task.Run虽然会将任务投递到线程池,但外层try-catch无法捕获线程池内的异常,连接过程抛出的错误会被静默吞掉,代码实际未执行到触发配对的socket.Connect()逻辑。
  • UUID获取逻辑错误:未配对设备在刚扫描完成时,本地缓存的UUID列表可能为空或不完整,直接在连接阶段调用dev.GetUuids()取第一个UUID创建socket,大概率拿到的是无效服务UUID,无法建立有效RFCOMM连接。
  • 扫描未完全终止:日志中cancelDiscovery = false说明发起连接时蓝牙扫描仍在运行,Android系统会直接拦截扫描过程中的连接请求,不会触发配对流程。
  • 权限缺失:Android 12(API 31)及以上版本需要动态申请BLUETOOTH_CONNECT权限,权限未授予时系统会静默拒绝配对请求,不会弹出配对窗口。
  • 线程操作违规:原有代码在子线程中直接调用Toast弹提示,会触发Android线程访问异常,导致连接流程中断。
修复方案
  1. 修正线程启动逻辑:不要直接调用Run()方法,调用Start()方法启动独立工作线程,移除多余的Task.Run嵌套,保证异常可以被正常捕获。
  2. 提前拉取设备UUID:在扫描发现设备阶段,调用FetchUuidsWithSdp()主动拉取设备的完整服务UUID列表,不要在连接时临时读取缓存值。
  3. 连接前强制终止扫描:发起连接前先调用BluetoothAdapter.DefaultAdapter.CancelDiscovery(),确认扫描终止后再执行连接逻辑。
  4. 补全权限配置:在AndroidManifest中声明对应版本需要的蓝牙权限,在页面启动时动态申请BLUETOOTH_CONNECT权限,权限授予后再允许用户点击设备发起连接。
  5. 修正线程UI操作:所有Toast等UI操作必须切到主线程执行,避免子线程访问UI导致的崩溃。

核心修复代码参考:

// 设备点击事件中先终止扫描再启动线程
BluetoothAdapter.DefaultAdapter.CancelDiscovery();
BluetoothThread thread = new BluetoothThread(_mydevice,this);
// 调用Start()启动新线程,不要直接调用Run()
thread.Start();
public override void Run()
{
    try
    {
        ParcelUuid[] parcelUuids = dev.GetUuids();
        // 优先匹配标准SPP服务UUID,不要直接取第一个UUID
        UUID targetUuid = parcelUuids.FirstOrDefault(u => 
            u.Uuid.ToString().Equals("00001101-0000-1000-8000-00805F9B34FB"))?.Uuid 
            ?? parcelUuids[0].Uuid;
        BluetoothSocket socket = dev.CreateRfcommSocketToServiceRecord(targetUuid);
        socket.Connect();
        // 连接成功后的后续业务逻辑
    }
    catch (Exception p)
    {
        // 切到主线程弹出错误提示
        Handler mainHandler = new Handler(Looper.MainLooper);
        mainHandler.Post(() => Toast.MakeText(ctx,p.Message,ToastLength.Short).Show());
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.01 15:31:57