关于 maui-permissions
面向 .NET MAUI 开发者的权限处理指南,整理常见陷阱与推荐模式,涵盖 iOS、Android 和 Windows 的平台差异。
主要功能
- 01请求前检查权限状态
- 02处理拒绝、受限和有限状态
- 03考虑 iOS 与 Android 行为差异
- 04使用平台对应的权限声明
适用场景
- 01避免重复请求已授予的权限
- 02拒绝后引导 iOS 用户前往设置
- 03重新请求前显示 Android 说明
- 04在 Android 13+ 选择媒体权限
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ davidortinau/ maui-skills --skill maui-permissions安装后在 Claude Code 中输入/maui-permissions确认已加载。
附带文件references/permissions-api.md
详细介绍
.NET MAUI 权限——注意事项与最佳实践
关键反模式
1. 未先检查就请求
// ❌ Shows prompt even if already granted
var status = await Permissions.RequestAsync<Permissions.Camera>();
// ✅ Check first — avoids unnecessary prompts
var status = await Permissions.CheckStatusAsync<Permissions.Camera>();
if (status != PermissionStatus.Granted)
status = await Permissions.RequestAsync<Permissions.Camera>();
2. 在构造函数中调用权限 API
权限 API 是异步的,需要 UI 上下文。构造函数中无法使用 await。
// ❌ Blocks the UI thread or throws
public MyViewModel()
{
var status = Permissions.RequestAsync<Permissions.Camera>().Result;
}
// ✅ Use OnAppearing, a command, or async initialization
protected override async void OnAppearing()
{
await CheckAndRequestPermissionAsync<Permissions.Camera>();
}
3. 忽略 Denied 和 Restricted 状态
// ❌ Only checks for Granted — misses edge cases
if (status == PermissionStatus.Granted) { /* proceed */ }
// ✅ Handle all relevant statuses
switch (status)
{
case PermissionStatus.Granted: /* proceed */ break;
case PermissionStatus.Limited: /* iOS partial access */ break;
case PermissionStatus.Denied:
case PermissionStatus.Restricted:
await ShowSettingsPromptAsync(); break;
}
平台注意事项
⚠️ iOS:权限对话框只显示一次
iOS 对每种权限只会显示一次系统权限对话框。一旦用户拒绝,RequestAsync 就会立即返回 Denied,而不会显示界面。你必须引导用户前往设置 → App → 权限。
⚠️ Android:ShouldShowRationale 的调用时机
ShouldShowRationale 仅会在用户之前拒绝过权限后返回 true(但用户选择“不再询问”后不会)。再次请求前,可使用它显示说明界面:
if (Permissions.ShouldShowRationale<Permissions.Camera>())
{
await Shell.Current.DisplayAlert("Permission needed",
"Camera access is required to scan barcodes.", "OK");
}
status = await Permissions.RequestAsync<Permissions.Camera>();
⚠️ Android API 33+:StorageRead/StorageWrite 已失效
在 Android 13+ 上,StorageRead 和 StorageWrite 始终返回 Granted(分区存储使它们失去意义)。请改用细分的媒体权限:
// ❌ Always returns Granted on API 33+ — gives false confidence
await Permissions.RequestAsync<Permissions.StorageRead>();
// ✅ Use the specific media permission
await Permissions.RequestAsync<Permissions.Photos>(); // photo access
await Permissions.RequestAsync<Permissions.Media>(); // audio/video
⚠️ Windows:大多数权限始终返回 Granted
Windows 的大多数功能没有运行时权限对话框。请改为在 Package.appxmanifest 中声明功能。
始终先检查再请求的模式
推荐的模式可处理 iOS 权限对话框只显示一次的情况,以及 Android 的理由提示:
public async Task<PermissionStatus> CheckAndRequestPermissionAsync<T>()
where T : Permissions.BasePermission, new()
{
var status = await Permissions.CheckStatusAsync<T>();
if (status == PermissionStatus.Granted)
return status;
if (status == PermissionStatus.Denied && DeviceInfo.Platform == DevicePlatform.iOS)
return status; // iOS won't show dialog again
if (Permissions.ShouldShowRationale<T>())
{
await Shell.Current.DisplayAlert("Permission needed",
"This feature requires the requested permission.", "OK");
}
return await Permissions.RequestAsync<T>();
}
检查清单
- 每次调用
RequestAsync前都调用CheckStatusAsync - 不在构造函数中调用权限 API——使用
OnAppearing或命令 - 处理所有
PermissionStatus值(Denied、Restricted、Limited) - Android:再次请求前显示
ShouldShowRationale提示 - iOS:为被拒绝的权限提供前往“设置”的引导
- Android API 33+:使用
Photos/Media,而不是StorageRead/StorageWrite - 清单/plist 声明与运行时权限请求相匹配
评论
支持 Markdown