已配置蓝牙权限仍遭Android Studio权限检查提示的求助
我用HC-05模块实现Arduino向Android传输串口数据,已经在AndroidManifest.xml配置了蓝牙权限,也在MainActivity里加了权限申请和onRequestPermissionsResult处理逻辑,但调用bluetoothSocket.createRfcommSocketToServiceRecord(MY_UUID)和bluetoothSocket.connect()时,Android Studio仍提示:
Call requires permission which may be rejected by user: code should explicitly check to see if permission is available (with
checkPermission) or explicitly handle a potentialSecurityException
尝试添加权限申请结果处理和前置判断后错误依然存在,以下是具体解决步骤:
问题根源
代码仅适配了Android 11及以下的旧版蓝牙权限(BLUETOOTH、BLUETOOTH_ADMIN),但Android 12(API 31)及以上引入了细分权限BLUETOOTH_CONNECT和BLUETOOTH_SCAN;同时initBluetooth方法内未再次确认权限状态(用户可能在运行时撤销权限),导致IDE检测到权限风险。
具体修复步骤
1. 动态适配不同版本的权限数组
根据系统版本返回对应权限,替代原固定数组:
private static final int REQUEST_BLUETOOTH_PERMISSIONS = 1; // 动态获取对应版本的蓝牙权限 private String[] getBluetoothPermissions() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { return new String[]{ Manifest.permission.BLUETOOTH_CONNECT, Manifest.permission.BLUETOOTH_SCAN }; } else { return new String[]{ Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.ACCESS_FINE_LOCATION }; } }
需导入
android.os.Build包
2. 更新权限检查与申请逻辑
修改onCreate中的权限判断,覆盖所有需要的权限:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); fastestTextView = findViewById(R.id.fastest_text_view); slowestTextView = findViewById(R.id.slowest_text_view); averageTextView = findViewById(R.id.average_text_view); // 检查所有必要权限是否已授予 boolean hasAllPermissions = true; for (String permission : getBluetoothPermissions()) { if (ActivityCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) { hasAllPermissions = false; break; } } if (!hasAllPermissions) { ActivityCompat.requestPermissions(this, getBluetoothPermissions(), REQUEST_BLUETOOTH_PERMISSIONS); } else { initBluetooth(); } }
3. 在initBluetooth开头添加二次权限检查
即使权限已申请通过,也要在调用蓝牙API前再次确认(防止用户中途撤销):
private void initBluetooth() { // 二次检查权限,避免运行时权限被撤销 boolean hasAllPermissions = true; for (String permission : getBluetoothPermissions()) { if (ActivityCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) { hasAllPermissions = false; break; } } if (!hasAllPermissions) { ActivityCompat.requestPermissions(this, getBluetoothPermissions(), REQUEST_BLUETOOTH_PERMISSIONS); return; } // 原有蓝牙初始化逻辑 bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); if (bluetoothAdapter == null) { Toast.makeText(this, "蓝牙不被设备支持", Toast.LENGTH_SHORT).show(); finish(); return; } if (!bluetoothAdapter.isEnabled()) { Toast.makeText(this, "请开启蓝牙", Toast.LENGTH_SHORT).show(); finish(); return; } bluetoothDevice = bluetoothAdapter.getRemoteDevice(MAC_ADDRESS); try { bluetoothSocket = bluetoothDevice.createRfcommSocketToServiceRecord(MY_UUID); bluetoothSocket.connect(); inputStream = bluetoothSocket.getInputStream(); reader = new BufferedReader(new InputStreamReader(inputStream)); new Thread(new Runnable() { @Override public void run() { while (true) { try { final String data = reader.readLine(); runOnUiThread(new Runnable() { @Override public void run() { fastestTextView.setText(data); slowestTextView.setText(data); averageTextView.setText(data); } }); } catch (IOException e) { e.printStackTrace(); } } } }).start(); } catch (IOException e) { e.printStackTrace(); } catch (SecurityException e) { // 显式捕获权限不足异常,避免崩溃 Toast.makeText(this, "蓝牙权限不足,无法连接", Toast.LENGTH_SHORT).show(); e.printStackTrace(); } }
4. 更新权限申请结果处理逻辑
确保所有权限都被授予才继续执行:
@Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == REQUEST_BLUETOOTH_PERMISSIONS) { boolean allGranted = true; for (int result : grantResults) { if (result != PackageManager.PERMISSION_GRANTED) { allGranted = false; break; } } if (allGranted) { initBluetooth(); } else { Toast.makeText(this, "需要蓝牙权限才能继续", Toast.LENGTH_SHORT).show(); finish(); } } }
额外说明
- Android 12+的
BLUETOOTH_CONNECT是连接已配对设备的核心权限,BLUETOOTH_SCAN用于设备扫描(若你的流程不需要扫描可移除) - 显式捕获
SecurityException可避免程序崩溃,同时给用户明确提示 - 权限检查需覆盖所有调用蓝牙API的入口,防止权限状态变化引发异常
内容的提问来源于stack exchange,提问作者developpy97

