.NET MAUI Android蓝牙权限请求异常及位置权限疑问
.NET MAUI Android蓝牙权限问题排查与解决
我用.NET MAUI开发跨Android/iOS的蓝牙应用,基于Plugin.BLE实现通信,功能正常但Android端权限流程有问题:
- Android 12及以上需要
BLUETOOTH_SCAN和BLUETOOTH_CONNECT权限,Android 12及以下还需位置权限 - 现在的代码每次启动只弹出一个权限申请:首次启动请求位置权限,扫描蓝牙直接崩溃;第二次启动请求蓝牙扫描权限,Android12+因缺
BLUETOOTH_CONNECT再次崩溃;第三次启动才能凑齐所有权限恢复正常
需要解决:
- 哪里操作错了?怎么实现首次启动一次性请求所有必要权限,避免用户多次重启?
- 蓝牙功能是否必须依赖位置权限?Android9上没位置权限就没法用。
我的MainActivity.cs
public class MainActivity : MauiAppCompatActivity { private const int REQUEST_FINE_LOCATION_PERMISSION = 100; private const int REQUEST_BLUETOOTH_SCAN_PERMISSION = 101; private const int REQUEST_BACKGROUND_LOCATION_PERMISSION = 102; private const int REQUEST_BLUETOOTH_CONNECT_PERMISSION = 103; protected override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); RequestedOrientation = ScreenOrientation.Portrait; // Request the ACCESS_FINE_LOCATION permission at runtime if (CheckSelfPermission(Manifest.Permission.AccessFineLocation) != Permission.Granted) { RequestPermissions(new string[] { Manifest.Permission.AccessFineLocation }, REQUEST_FINE_LOCATION_PERMISSION); } //Request the ACCESS_BACKGROUND_LOCATION permission at runtime if (CheckSelfPermission(Manifest.Permission.AccessBackgroundLocation) != Permission.Granted) { RequestPermissions(new string[] { Manifest.Permission.AccessBackgroundLocation }, REQUEST_BACKGROUND_LOCATION_PERMISSION); } // Request the BLUETOOTH_SCAN permission at runtime if (CheckSelfPermission(Manifest.Permission.BluetoothScan) != Permission.Granted) { RequestPermissions(new string[] { Manifest.Permission.BluetoothScan }, REQUEST_BLUETOOTH_SCAN_PERMISSION); } //Request the BLUETOOTH_CONNECT permission at runtime if (CheckSelfPermission(Manifest.Permission.BluetoothConnect) != Permission.Granted) { RequestPermissions(new string[] { Manifest.Permission.BluetoothConnect }, REQUEST_BLUETOOTH_CONNECT_PERMISSION); } } public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults) { base.OnRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == REQUEST_FINE_LOCATION_PERMISSION) { if (grantResults.Length > 0 && grantResults[0] == Permission.Granted) { // ACCESS_FINE_LOCATION permission granted, you can now scan for Bluetooth devices } else { // ACCESS_FINE_LOCATION permission denied, you can't scan for Bluetooth devices } } if (requestCode == REQUEST_BACKGROUND_LOCATION_PERMISSION) { if (grantResults.Length > 0 && grantResults[0] == Permission.Granted) { // ACCESS_BACKGROUND_LOCATION permission granted, you can now scan for Bluetooth devices in the background } else { // ACCESS_BACKGROUND_LOCATION permission denied, you can't scan for Bluetooth devices in the background } } if (requestCode == REQUEST_BLUETOOTH_SCAN_PERMISSION) { if (grantResults.Length > 0 && grantResults[0] == Permission.Granted) { // BLUETOOTH_SCAN permission granted, you can now scan for Bluetooth devices } else { // BLUETOOTH_SCAN permission denied, you can't scan for Bluetooth devices } } if (requestCode == REQUEST_BLUETOOTH_CONNECT_PERMISSION) { if (grantResults.Length > 0 && grantResults[0] == Permission.Granted) { // BLUETOOTH_SCAN permission granted, you can now scan for Bluetooth devices } else { // BLUETOOTH_SCAN permission denied, you can't scan for Bluetooth devices } } } }
我的AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application android:allowBackup="true" android:icon="@mipmap/appicon" android:roundIcon="@mipmap/appicon_round" android:supportsRtl="true"></application> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation"/> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" /> <uses-feature android:name="android.hardware.bluetooth" android:required="false"/> <uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/> </manifest>
问题分析与解决方案
1. 为什么每次只弹出一个权限申请?
你当前代码多次独立调用RequestPermissions,每次仅传单个权限。Android系统权限机制规定:同一生命周期内多次触发权限请求时,系统只会优先处理第一个,后续请求会被忽略或延迟到当前请求处理完成后,无法批量弹出。因此必须将所有必要权限合并到同一个数组中,一次性发起请求。
2. 修正后的权限请求逻辑
需根据Android版本区分权限集合,避免冗余请求:
- Android 12(API 31)及以上:仅需
BLUETOOTH_SCAN、BLUETOOTH_CONNECT(已添加neverForLocation标记,无需位置权限) - Android 11(API 30)及以下:需
ACCESS_FINE_LOCATION(或ACCESS_COARSE_LOCATION)+ 旧蓝牙权限BLUETOOTH、BLUETOOTH_ADMIN
修改后的MainActivity.cs代码:
public class MainActivity : MauiAppCompatActivity { private const int REQUEST_ALL_PERMISSIONS = 100; protected override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); RequestedOrientation = ScreenOrientation.Portrait; RequestRequiredPermissions(); } private void RequestRequiredPermissions() { var permissionsToRequest = new List<string>(); // 根据Android版本判断需要的权限 if (Build.VERSION.SdkInt >= BuildVersionCodes.S) { // Android 12+ if (CheckSelfPermission(Manifest.Permission.BluetoothScan) != Permission.Granted) permissionsToRequest.Add(Manifest.Permission.BluetoothScan); if (CheckSelfPermission(Manifest.Permission.BluetoothConnect) != Permission.Granted) permissionsToRequest.Add(Manifest.Permission.BluetoothConnect); } else { // Android 11及以下 if (CheckSelfPermission(Manifest.Permission.AccessFineLocation) != Permission.Granted) permissionsToRequest.Add(Manifest.Permission.AccessFineLocation); // 旧蓝牙权限在API31+已废弃,但低版本需要 if (CheckSelfPermission(Manifest.Permission.Bluetooth) != Permission.Granted) permissionsToRequest.Add(Manifest.Permission.Bluetooth); if (CheckSelfPermission(Manifest.Permission.BluetoothAdmin) != Permission.Granted) permissionsToRequest.Add(Manifest.Permission.BluetoothAdmin); } // 仅当需要后台扫描时添加后台位置权限(按需启用) //if (Build.VERSION.SdkInt >= BuildVersionCodes.Q && CheckSelfPermission(Manifest.Permission.AccessBackgroundLocation) != Permission.Granted) // permissionsToRequest.Add(Manifest.Permission.AccessBackgroundLocation); if (permissionsToRequest.Any()) { RequestPermissions(permissionsToRequest.ToArray(), REQUEST_ALL_PERMISSIONS); } } public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults) { base.OnRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode != REQUEST_ALL_PERMISSIONS) return; // 检查所有权限是否都已授予 var allGranted = grantResults.All(result => result == Permission.Granted); if (allGranted) { // 所有权限已获取,初始化蓝牙功能 } else { // 处理权限被拒绝的情况,比如引导用户前往设置开启权限 } } }
3. 关于位置权限的必要性
- Android 6.0(API23)~ Android 11(API30):系统强制要求位置权限才能扫描BLE设备,因为蓝牙扫描可被用于推测用户位置(如周边Beacon),属于系统安全限制。
- Android 12(API31)及以上:新增
BLUETOOTH_SCAN权限,配合Manifest中的neverForLocation标记,可无需位置权限完成扫描,系统会识别你不通过蓝牙获取位置。 - Android 9:属于API28,处于强制要求位置权限的版本区间,无位置权限确实无法扫描蓝牙,无法绕过系统限制。
额外优化点
- Manifest中
neverForLocation标记已正确配置,Android12+可正常免位置权限扫描。 - 若无需后台扫描蓝牙,可移除
ACCESS_BACKGROUND_LOCATION的权限请求与Manifest声明。 - 权限被拒绝后,应引导用户前往应用设置页开启权限,而非依赖用户重启应用。
内容的提问来源于stack exchange,提问作者Baddack
相关产品推荐
相关产品推荐

