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

Android 13及以上版本蓝牙配对设备连接崩溃权限问题求助

解决方案:Android 13+蓝牙通信权限与连接问题修复

一、权限请求的正确实现(Android 13+适配)

Android 13(API 33)将原有的蓝牙权限拆分为BLUETOOTH_SCAN、BLUETOOTH_CONNECT、BLUETOOTH_ADVERTISE,权限请求逻辑和时机必须调整:

1. Manifest权限配置

确保配置覆盖全版本,区分前后台权限:

<!-- Android 11及以下基础权限 -->
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />

<!-- Android 12+ 蓝牙权限 -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

<!-- 位置权限(仅Android 11及以下需要,扫描无需位置时可限制版本) -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="31" />

<!-- 蓝牙硬件声明 -->
<uses-feature android:name="android.hardware.bluetooth" android:required="true" />
<uses-feature android:name="android.hardware.bluetooth_le" android:required="false" />

2. 运行时权限请求的两种方式

除了你当前用的方法,还可以用AndroidX的ActivityResultContracts(推荐)或传统的requestPermissions():

方式一:ActivityResultContracts(灵活适配)

// 请求扫描权限(Android 12+可跳过位置权限)
private void requestScanPermissions() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        List<String> permissions = new ArrayList<>();
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
            permissions.add(Manifest.permission.BLUETOOTH_SCAN);
        }
        // 扫描无需位置时添加该参数,避免弹出位置权限弹窗
        Intent intent = new Intent(Settings.ACTION_REQUEST_PERMISSIONS);
        intent.putExtra(Settings.EXTRA_PERMISSIONS, permissions.toArray(new String[0]));
        intent.putExtra("android.bluetooth.adapter.extra.USE_LOCATION", false);
        startActivityForResult(intent, REQUEST_CODE_SCAN);
    } else {
        // 旧版本请求位置权限
        registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), result -> {
            boolean granted = result.getOrDefault(Manifest.permission.ACCESS_FINE_LOCATION, false);
            if (granted) startScan();
        }).launch(new String[]{Manifest.permission.ACCESS_FINE_LOCATION});
    }
}

// 请求连接权限
private void requestConnectPermissions() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        registerForActivityResult(new ActivityResultContracts.RequestPermission(), granted -> {
            if (granted) connectToDevice();
        }).launch(Manifest.permission.BLUETOOTH_CONNECT);
    } else {
        connectToDevice();
    }
}

方式二:传统requestPermissions()

private static final int REQUEST_BLUETOOTH_PERMS = 1001;

private void requestBluetoothPermissions() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        String[] perms = {Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_CONNECT};
        if (ContextCompat.checkSelfPermission(this, perms[0]) != PackageManager.PERMISSION_GRANTED
                || ContextCompat.checkSelfPermission(this, perms[1]) != PackageManager.PERMISSION_GRANTED) {
            requestPermissions(perms, REQUEST_BLUETOOTH_PERMS);
        }
    } else {
        String[] perms = {Manifest.permission.ACCESS_FINE_LOCATION};
        requestPermissions(perms, REQUEST_BLUETOOTH_PERMS);
    }
}

// 权限回调
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == REQUEST_BLUETOOTH_PERMS) {
        boolean allGranted = true;
        for (int result : grantResults) {
            if (result != PackageManager.PERMISSION_GRANTED) {
                allGranted = false;
                break;
            }
        }
        if (allGranted) {
            // 权限通过,初始化蓝牙
            initBluetooth();
        }
    }
}

二、逐个解决你的问题

1. 首次启动崩溃+无蓝牙开启弹窗

  • 崩溃原因:Android 13+未授予BLUETOOTH_CONNECT权限时,调用蓝牙API(如获取配对设备)会直接崩溃,且你可能在权限未授予前就执行了蓝牙操作。
  • 修复:启动后先请求权限,再初始化蓝牙;蓝牙开启必须通过系统Intent触发,禁止直接调用enable():
    private void enableBluetooth() {
        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
        if (adapter == null) return; // 设备不支持蓝牙
        if (!adapter.isEnabled()) {
            Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
        }
    }
    
    注意:必须在权限授予后调用此方法,否则会被系统拦截。

2. 连接时崩溃提示需要BLUETOOTH_SCAN权限

  • 原因:Android 13+连接配对设备时,部分场景仍需BLUETOOTH_SCAN权限,且你的权限检查逻辑遗漏了该权限。
  • 修复:连接前同时检查BLUETOOTH_CONNECT和BLUETOOTH_SCAN权限,两者都授予后再执行连接。

3. "socket可能已关闭或超时"错误

  • 核心原因:连接操作在主线程执行导致超时、权限不足导致Socket创建失败、Socket未正确关闭导致资源泄漏。
  • 修复:
    1. 强制在子线程执行连接操作:
      private void connectToDevice(BluetoothDevice device) {
          new Thread(() -> {
              BluetoothSocket socket = null;
              try {
                  // Arduino蓝牙模块通用UUID:00001101-0000-1000-8000-00805F9B34FB
                  UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
                  socket = device.createInsecureRfcommSocketToServiceRecord(uuid);
                  BluetoothAdapter.getDefaultAdapter().cancelDiscovery(); // 取消扫描释放资源
                  socket.connect();
                  // 连接成功,切换到UI线程更新状态
                  runOnUiThread(() -> Toast.makeText(this, "连接成功", Toast.LENGTH_SHORT).show());
              } catch (IOException e) {
                  runOnUiThread(() -> Toast.makeText(this, "连接失败:" + e.getMessage(), Toast.LENGTH_SHORT).show());
              } finally {
                  // 无论成败,关闭Socket避免泄漏
                  if (socket != null) {
                      try {
                          socket.close();
                      } catch (IOException ignored) {}
                  }
              }
          }).start();
      }
      
    2. 检查Arduino端蓝牙模块配置,确保广播的UUID与App一致,且模块处于可连接模式。

4. 连接蓝牙耳机后App能成功连接的异常情况

这是系统兼容性漏洞(蓝牙活跃时权限检查被绕过),不能作为解决方案,必须通过上述权限和连接逻辑修复才能保证稳定运行。

三、额外注意事项

  • Android 13+扫描无需位置时,必须在BLUETOOTH_SCAN请求中添加android.bluetooth.adapter.extra.USE_LOCATION=false,否则会强制弹出位置权限弹窗。
  • 禁止在后台执行蓝牙扫描或连接,Android 12+对后台蓝牙操作有严格限制,可能被系统杀死。
  • 测试前清除App权限缓存,确保权限请求流程正常触发。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 03:00:06