权限

Browse sample.浏览示例

本文介绍如何使用 .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
NetworkState
Phone
Photos
PhotosAddOnly
Reminders
Sensors
Sms
Speech
StorageRead
StorageWrite
Vibrate
权限 Android iOS Windows tvOS
Battery
蓝牙
CalendarRead
CalendarWrite
Camera
ContactsRead
ContactsWrite
Flashlight
LocationWhenInUse
LocationAlways
媒体
Microphone
NearbyWifiDevices
NetworkState
Phone
Photos
PhotosAddOnly
Reminders
Sensors
Sms
Speech
StorageRead
StorageWrite
Vibrate

如果权限标记为 ❌,则在检查或请求该权限时始终会返回 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