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

.NET MAUI Android蓝牙权限请求异常及位置权限疑问

.NET MAUI Android蓝牙权限问题排查与解决

我用.NET MAUI开发跨Android/iOS的蓝牙应用,基于Plugin.BLE实现通信,功能正常但Android端权限流程有问题:

  • Android 12及以上需要BLUETOOTH_SCAN和BLUETOOTH_CONNECT权限,Android 12及以下还需位置权限
  • 现在的代码每次启动只弹出一个权限申请:首次启动请求位置权限,扫描蓝牙直接崩溃;第二次启动请求蓝牙扫描权限,Android12+因缺BLUETOOTH_CONNECT再次崩溃;第三次启动才能凑齐所有权限恢复正常

需要解决:

  1. 哪里操作错了?怎么实现首次启动一次性请求所有必要权限,避免用户多次重启?
  2. 蓝牙功能是否必须依赖位置权限?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,处于强制要求位置权限的版本区间,无位置权限确实无法扫描蓝牙,无法绕过系统限制。

额外优化点

  1. Manifest中neverForLocation标记已正确配置,Android12+可正常免位置权限扫描。
  2. 若无需后台扫描蓝牙,可移除ACCESS_BACKGROUND_LOCATION的权限请求与Manifest声明。
  3. 权限被拒绝后,应引导用户前往应用设置页开启权限,而非依赖用户重启应用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 22:27:07