权限

浏览示例。 浏览示例

本文介绍如何使用 .NET Multi-platform App UI (.NET MAUI) Permissions 类。 此类允许在运行时检查和请求权限。 Permissions 类型可从 Microsoft.Maui.ApplicationModel 命名空间中获取。

可用权限

.NET MAUI 会尝试对尽可能多的权限进行抽象化处理。 但是,每个操作系统都有一组不同的权限。 尽管 API 允许获取常见权限,但与该权限相关的操作系统之间可能存在差异。 下表介绍了可用的权限:

下表使用 ✔️ 来指示受支持的权限,使用 ❌ 来指示不受支持或不需要的权限:

权限 Android iOS Windows tvOS
Battery ✔️
蓝牙 ✔️
CalendarRead ✔️ ✔️
CalendarWrite ✔️ ✔️
Camera ✔️ ✔️
ContactsRead ✔️ ✔️
ContactsWrite ✔️ ✔️
Flashlight ✔️
LocationWhenInUse ✔️ ✔️ ✔️
LocationAlways ✔️ ✔️
媒体 ✔️
Microphone ✔️ ✔️
NearbyWifiDevices ✔️
NetworkState ✔️
Phone ✔️ ✔️
Photos ✔️ ✔️
PhotosAddOnly ✔️ ✔️
PostNotifications ✔️
Reminders ✔️
Sensors ✔️ ✔️
Sms ✔️ ✔️
Speech ✔️ ✔️
StorageRead ✔️
StorageWrite ✔️
Vibrate ✔️

重要

StorageReadStorageWrite 权限始终在 Android API 33+ 上返回Granted。 这是因为基础 Android READ_EXTERNAL_STORAGEWRITE_EXTERNAL_STORAGE 权限不再可从 API 33 获取。

如果权限标记为 ❌,则在检查或请求时将始终返回 Granted

检查权限

若要检查权限的当前状态,请使用 Permissions.CheckStatusAsync 方法以及用户获取其状态的特定权限。 以下示例查看 LocationWhenInUse 权限的状态:

PermissionStatus status = await Permissions.CheckStatusAsync<Permissions.LocationWhenInUse>();

如果未声明所需权限,则会引发 PermissionException

在请求权限之前,最好先检查该权限的状态。 如果系统从不提示用户,则每个操作系统都会返回不同的默认状态。 iOS 会返回 Unknown,其他系统会返回 Denied。 如果状态为 Granted,则无需进行其他调用。 在 iOS 上,如果状态为 Denied,则应提示用户在设置中更改权限。 在 Android 上,可以调用 ShouldShowRationale 来检测用户过去是否已拒绝此权限。

权限状态

使用 CheckStatusAsyncRequestAsync 时,将返回可用于确定后续步骤的 PermissionStatus

  • Unknown
    权限处于未知状态,或在 iOS 上,系统从不提示用户。

  • Denied
    用户拒绝了权限请求。

  • Disabled
    已在设备上禁用此功能。

  • Granted
    用户授予的权限或自动授予的权限。

  • Restricted
    处于受限状态。

  • Limited
    处于有限状态。 仅 iOS 返回此状态。

请求权限

若要向用户请求权限,请使用 RequestAsync 方法以及要请求的特定权限。 如果用户此前已授予权限,且尚未将其撤消,则此方法将返回 Granted,而不向用户显示对话框。 不应从你的 MauiProgramApp 类请求权限,并且只应在应用的第一页出现后请求权限。

以下示例请求 LocationWhenInUse 权限:

PermissionStatus status = await Permissions.RequestAsync<Permissions.LocationWhenInUse>();

如果未声明所需权限,则会引发 PermissionException

重要说明

在某些平台上,权限请求只能激活一次。 开发者必须处理其他提示,以检查权限是否处于 Denied 状态,并要求用户手动将其开启。

说明为何需要权限

最佳做法是,向用户说明应用程序需要特定权限的原因。 在 iOS 上,必须指定向用户显示的字符串。 Android 没有此功能,并且将权限状态默认设置为 Disabled。 这样就无法了解用户是否拒绝了权限,也无法了解是否是首次请求权限。 可使用 ShouldShowRationale 方法确定是否应显示信息性 UI。 如果该方法返回 true,则是因为过去用户已拒绝或禁用该权限。 调用此方法时,其他平台将始终返回 false

示例

以下代码演示了用于确定是否已授予权限(如果尚未授予权限,则请求该权限)的常规使用模式。

public async Task<PermissionStatus> CheckAndRequestLocationPermission()
{
    PermissionStatus status = await Permissions.CheckStatusAsync<Permissions.LocationWhenInUse>();

    if (status == PermissionStatus.Granted)
        return status;

    if (status == PermissionStatus.Denied && DeviceInfo.Platform == DevicePlatform.iOS)
    {
        // Prompt the user to turn on in settings
        // On iOS once a permission has been denied it may not be requested again from the application
        return status;
    }

    if (Permissions.ShouldShowRationale<Permissions.LocationWhenInUse>())
    {
        // Prompt the user with additional information as to why the permission is needed
    }

    status = await Permissions.RequestAsync<Permissions.LocationWhenInUse>();

    return status;
}

扩展权限

权限 API 灵活且可扩展,适用于需要额外验证或 .NET MAUI 中未包含权限的应用。 创建继承自 Permissions.BasePermission 的类,并实现所需的抽象方法。 以下示例代码展示了基本抽象成员,但没有实现:

public class MyPermission : Permissions.BasePermission
{
    // This method checks if current status of the permission.
    public override Task<PermissionStatus> CheckStatusAsync()
    {
        throw new System.NotImplementedException();
    }

    // This method is optional and a PermissionException is often thrown if a permission is not declared.
    public override void EnsureDeclared()
    {
        throw new System.NotImplementedException();
    }

    // Requests the user to accept or deny a permission.
    public override Task<PermissionStatus> RequestAsync()
    {
        throw new System.NotImplementedException();
    }

    // Indicates that the requestor should prompt the user as to why the app requires the permission, because the
    // user has previously denied this permission.
    public override bool ShouldShowRationale()
    {
        throw new NotImplementedException();
    }
}

在特定平台中实现权限时,可以继承自 Permissions.BasePlatformPermission 类。 此类提供额外的平台帮助程序方法,用于自动检查权限声明。 这在创建执行分组的自定义权限时会有所帮助,例如:请求对 Android 上存储空间的读取写入访问权限。 以下代码示例展示请求对存储空间的读取写入访问权限:

public class ReadWriteStoragePerms : Permissions.BasePlatformPermission
{
    public override (string androidPermission, bool isRuntime)[] RequiredPermissions =>
        new List<(string androidPermission, bool isRuntime)>
        {
        (global::Android.Manifest.Permission.ReadExternalStorage, true),
        (global::Android.Manifest.Permission.WriteExternalStorage, true)
        }.ToArray();
}

然后,按照与 .NET MAUI 提供的任何其他权限类型相同的方式检查权限:

PermissionStatus status = await Permissions.RequestAsync<ReadWriteStoragePerms>();

如果想要从跨平台代码调用此 API,可以创建一个接口,并将自定义权限作为依赖项注册至应用的服务容器中。 以下示例展示了 IReadWritePermission 接口:

public interface IReadWritePermission
{        
    Task<PermissionStatus> CheckStatusAsync();
    Task<PermissionStatus> RequestAsync();
}

然后以自定义权限实现接口:

public class ReadWriteStoragePermission : Permissions.BasePlatformPermission, IReadWritePermission
{
    public override (string androidPermission, bool isRuntime)[] RequiredPermissions => new List<(string androidPermission, bool isRuntime)>
    {
        (Android.Manifest.Permission.ReadExternalStorage, true),
        (Android.Manifest.Permission.WriteExternalStorage, true)
    }.ToArray();
}

然后在 MauiProgram 类中,应当在应用的服务容器中注册接口及其具体类型以及将使用自定义权限的类型:

builder.Services.AddTransient<MyViewModel>();
builder.Services.AddSingleton<IReadWritePermission, ReadWriteStoragePermission>();

然后,可以对自定义权限实现进行解析,并且从其中一种类型(例如 viewmodel)对其调用:

public class MyViewModel
{
    IReadWritePermission _readWritePermission;

    public MyViewModel(IReadWritePermission readWritePermission)
    {
        _readWritePermission = readWritePermission;
    }

    public async Task CheckPermissionAsync()
    {
        var status = await _readWritePermission.CheckStatusAsync();
        if (status != PermissionStatus.Granted)
        {
            status = await _readWritePermission.RequestAsync();
        }
    }
}

平台差异

本部分介绍与权限 API 相关的特定于平台的差异。

权限必须在 Android 清单文件中设置有匹配的属性。 权限状态默认为 Denied