关于 maui-local-notifications
.NET MAUI 本地通知实现指南,面向需要跨平台发送或安排通知的开发者。它汇总 Android、iOS、Mac Catalyst 与 Windows 的实现要点和平台限制。
主要功能
- 01跨平台通知服务设计
- 02Android 通知权限与渠道配置
- 03Android 重启后重新安排通知
- 04iOS 与 Mac Catalyst 前台通知处理
- 05Windows 定时通知限制
适用场景
- 01实现即时本地通知
- 02安排提醒并处理 Android 重启
- 03排查通知权限或前台显示问题
- 04根据平台能力规划通知功能
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ davidortinau/ maui-skills --skill maui-local-notifications安装后在 Claude Code 中输入/maui-local-notifications确认已加载。
附带文件references/local-notifications-api.md
详细介绍
.NET MAUI 本地通知
实现概述
- 定义跨平台的
INotificationManagerService接口和事件参数 - 实现 Android 通知服务(通道、AlarmManager、BroadcastReceiver)
- 实现 iOS/Mac Catalyst 通知服务(UNUserNotificationCenter)
- 通过 DI 注册平台实现
- 配置平台权限和 MainActivity
完整实现代码请参见 references/local-notifications-api.md。
平台注意事项
Android
| 问题 | 解决方法 |
|---|---|
| API 33+ 上通知静默失败 | 必须先请求 POST_NOTIFICATIONS 运行时权限 |
Android 12+ 上 PendingIntent 崩溃 | API 31+ 必须包含 PendingIntentFlags.Immutable |
| 设备重启后计划通知丢失 | AlarmManager 无法在设备重启后保留 — 通过 BOOT_COMPLETED 接收器在启动时重新安排 |
| 未显示通知 | 未创建通道 — API 26+(Android 8.0)必需 |
| 点击通知后未返回应用 | 必须在 MainActivity 上设置 LaunchMode = LaunchMode.SingleTop |
// ✅ Correct — API 31+ requires Immutable flag
var pendingIntentFlags = (Build.VERSION.SdkInt >= BuildVersionCodes.S)
? PendingIntentFlags.CancelCurrent | PendingIntentFlags.Immutable
: PendingIntentFlags.CancelCurrent;
// ❌ Wrong — crashes on Android 12+
var pendingIntentFlags = PendingIntentFlags.CancelCurrent;
iOS / Mac Catalyst
| 问题 | 解决方法 |
|---|---|
| 未显示通知提示 | 权限已被拒绝 — 用户必须在“设置”中重新启用 |
| 前台通知未显示 | 必须实现 UNUserNotificationCenterDelegate 并设置 Current.Delegate |
通知显示为 Alert 而非 Banner | 在 iOS 14+ 上使用 UNNotificationPresentationOptions.Banner |
// ✅ iOS 14+ — use Banner
completionHandler(OperatingSystem.IsIOSVersionAtLeast(14)
? UNNotificationPresentationOptions.Banner
: UNNotificationPresentationOptions.Alert);
// ❌ Always using Alert — deprecated on iOS 14+
completionHandler(UNNotificationPresentationOptions.Alert);
Windows
⚠️ Windows App SDK 支持 toast 通知,但尚不支持计划通知。即时通知可正常使用。
DI 注册 — 仅平台特定实现
// ✅ Must use #if guards — there's no cross-platform implementation
#if ANDROID
builder.Services.AddTransient<INotificationManagerService,
Platforms.Android.NotificationManagerService>();
#elif IOS
builder.Services.AddTransient<INotificationManagerService,
Platforms.iOS.NotificationManagerService>();
#elif MACCATALYST
builder.Services.AddTransient<INotificationManagerService,
Platforms.MacCatalyst.NotificationManagerService>();
#endif
常见反模式
// ❌ Sending without checking permission — silent failure on API 33+
notificationManager.SendNotification("Title", "Body");
// ✅ Request permission first
#if ANDROID
var status = await Permissions.RequestAsync<Platforms.Android.NotificationPermission>();
if (status != PermissionStatus.Granted) return;
#endif
// ❌ Updating UI directly from notification callback — cross-thread exception
notificationManager.NotificationReceived += (s, e) =>
myLabel.Text = ((NotificationEventArgs)e).Title;
// ✅ Marshal to UI thread
notificationManager.NotificationReceived += (s, e) =>
MainThread.BeginInvokeOnMainThread(() =>
myLabel.Text = ((NotificationEventArgs)e).Title);
决策框架
| 需求 | 方案 |
|---|---|
| 即时通知 | 使用 null notifyTime 调用 SendNotification(title, message) |
| 计划提醒 | 调用 SendNotification(title, message, DateTime.Now.AddMinutes(30)) |
| 重启后保留(Android) | 添加 BOOT_COMPLETED 接收器以重新安排闹钟 |
| 丰富通知(图片、操作) | 使用原生 API 扩展平台实现 |
| 来自服务端的推送通知 | 使用完全不同的模式(FCM/APNs) |
快速检查清单
- 已定义跨平台的
INotificationManagerService接口 - Android:已创建通知通道(API 26+)
- Android:清单中包含
POST_NOTIFICATIONS权限,并请求运行时权限(API 33+) - Android:已使用
PendingIntentFlags.Immutable(API 31+) - Android:
MainActivity设置了LaunchMode.SingleTop,并处理OnNewIntent - iOS:已设置
UNUserNotificationCenterDelegate以显示前台通知 - iOS:在 iOS 14+ 上使用
Banner而非Alert - DI 注册使用
#if平台保护 - 通知回调触发的 UI 更新使用
MainThread.BeginInvokeOnMainThread
评论
支持 Markdown