• 基于位置的触发器
  • 每个平台都需要自己的本机代码实现来发送和接收本地通知。 但是,每个平台实现都可以在跨平台层进行抽象化,以便在 .NET 多平台应用 UI (.NET MAUI) 应用中发送和接收本地通知有一致的 API。

    创建跨平台抽象

    .NET MAUI 应用应发送和接收本地通知,而无需担心基础平台实现。 以下 INotificationManagerService 接口用于定义可用于与本地通知交互的跨平台 API:

    public interface INotificationManagerService
        event EventHandler NotificationReceived;
        void SendNotification(string title, string message, DateTime? notifyTime = null);
        void ReceiveNotification(string title, string message);
    

    此接口定义以下操作:

  • NotificationReceived 事件允许应用处理传入通知。
  • SendNotification 方法会在可选 DateTime 发送通知。
  • ReceiveNotification 方法会在基础平台收到通知时,对其进行处理。
  • 应在想要支持本地通知的每个平台上实现该接口。

    在 Android 上实现本地通知

    在 Android 上的本地通知是在应用的 UI 外部显示的消息,用于提供应用中的提醒或其他信息。 用户可以点击该通知以打开应用,也可以选择直接从通知执行操作。 有关 Android 上的本地通知的信息,请参阅 developer.android.com 上的通知概述

    若要使 .NET MAUI 应用在 Android 上发送和接收通知,应用必须提供 INotificationManagerService 接口的实现。

    发送和接收本地通知

    在 Android 上,NotificationManagerService 类会实现 INotificationManagerService 接口,并包含用于发送和接收本地通知的逻辑:

    using Android.App;
    using Android.Content;
    using Android.Graphics;
    using Android.OS;
    using AndroidX.Core.App;
    namespace LocalNotificationsDemo.Platforms.Android;
    public class NotificationManagerService : INotificationManagerService
        const string channelId = "default";
        const string channelName = "Default";
        const string channelDescription = "The default channel for notifications.";
        public const string TitleKey = "title";
        public const string MessageKey = "message";
        bool channelInitialized = false;
        int messageId = 0;
        int pendingIntentId = 0;
        NotificationManagerCompat compatManager;
        public event EventHandler NotificationReceived;
        public static NotificationManagerService Instance { get; private set; }
        public NotificationManagerService()
            if (Instance == null)
                CreateNotificationChannel();
                compatManager = NotificationManagerCompat.From(Platform.AppContext);
                Instance = this;
        public void SendNotification(string title, string message, DateTime? notifyTime = null)
            if (!channelInitialized)
                CreateNotificationChannel();
            if (notifyTime != null)
                Intent intent = new Intent(Platform.AppContext, typeof(AlarmHandler));
                intent.PutExtra(TitleKey, title);
                intent.PutExtra(MessageKey, message);
                intent.SetFlags(ActivityFlags.SingleTop | ActivityFlags.ClearTop);
                var pendingIntentFlags = (Build.VERSION.SdkInt >= BuildVersionCodes.S)
                    ? PendingIntentFlags.CancelCurrent | PendingIntentFlags.Immutable
                    : PendingIntentFlags.CancelCurrent;
                PendingIntent pendingIntent = PendingIntent.GetBroadcast(Platform.AppContext, pendingIntentId++, intent, pendingIntentFlags);
                long triggerTime = GetNotifyTime(notifyTime.Value);
                AlarmManager alarmManager = Platform.AppContext.GetSystemService(Context.AlarmService) as AlarmManager;
                alarmManager.Set(AlarmType.RtcWakeup, triggerTime, pendingIntent);
                Show(title, message);
        public void ReceiveNotification(string title, string message)
            var args = new NotificationEventArgs()
                Title = title,
                Message = message,
            NotificationReceived?.Invoke(null, args);
        public void Show(string title, string message)
            Intent intent = new Intent(Platform.AppContext, typeof(MainActivity));
            intent.PutExtra(TitleKey, title);
            intent.PutExtra(MessageKey, message);
            intent.SetFlags(ActivityFlags.SingleTop | ActivityFlags.ClearTop);
            var pendingIntentFlags = (Build.VERSION.SdkInt >= BuildVersionCodes.S)
                ? PendingIntentFlags.UpdateCurrent | PendingIntentFlags.Immutable
                : PendingIntentFlags.UpdateCurrent;
            PendingIntent pendingIntent = PendingIntent.GetActivity(Platform.AppContext, pendingIntentId++, intent, pendingIntentFlags);
            NotificationCompat.Builder builder = new NotificationCompat.Builder(Platform.AppContext, channelId)
                .SetContentIntent(pendingIntent)
                .SetContentTitle(title)
                .SetContentText(message)
                .SetLargeIcon(BitmapFactory.DecodeResource(Platform.AppContext.Resources, Resource.Drawable.dotnet_logo))
                .SetSmallIcon(Resource.Drawable.message_small);
            Notification notification = builder.Build();
            compatManager.Notify(messageId++, notification);  
        void CreateNotificationChannel()
            // Create the notification channel, but only on API 26+.
            if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
                var channelNameJava = new Java.Lang.String(channelName);
                var channel = new NotificationChannel(channelId, channelNameJava, NotificationImportance.Default)
                    Description = channelDescription
                // Register the channel
                NotificationManager manager = (NotificationManager)Platform.AppContext.GetSystemService(Context.NotificationService);
                manager.CreateNotificationChannel(channel);
                channelInitialized = true;
        long GetNotifyTime(DateTime notifyTime)
            DateTime utcTime = TimeZoneInfo.ConvertTimeToUtc(notifyTime);
            double epochDiff = (new DateTime(1970, 1, 1) - DateTime.MinValue).TotalSeconds;
            long utcAlarmTime = utcTime.AddSeconds(-epochDiff).Ticks / 10000;
            return utcAlarmTime; // milliseconds
    

    NotificationManagerService 类应放置在应用的“平台 > Android”文件夹中。 或者,可以根据自己的文件名和文件夹条件执行多目标,而不是使用“平台 > Android”文件夹。 有关详细信息,请参阅配置多目标

    Android 允许应用为通知定义多个通道。 NotificationManagerService 构造函数会创建用于发送通知的基本通道。 SendNotification 方法定义创建和发送通知所需的特定于平台的逻辑。 当收到消息并调用 NotificationReceived 事件处理程序时,Android OS 将调用 ReceiveNotification 方法。 有关详细信息,请参阅 developer.android.com 上的创建通知

    SendNotification 方法立即或在准确的 DateTime 创建一个本地通知。 可以使用 AlarmManager 类为准确的 DateTime 安排一个通知,且该通知将由从 BroadcastReceiver 类派生的对象接收:

    [BroadcastReceiver(Enabled = true, Label = "Local Notifications Broadcast Receiver")]
    public class AlarmHandler : BroadcastReceiver
        public override void OnReceive(Context context, Intent intent)
            if (intent?.Extras != null)
                string title = intent.GetStringExtra(NotificationManagerService.TitleKey);
                string message = intent.GetStringExtra(NotificationManagerService.MessageKey);
                NotificationManagerService manager = NotificationManagerService.Instance ?? new NotificationManagerService();
                manager.Show(title, message);
    

    默认情况下,使用 AlarmManager 类计划的通知在设备重启后不会得到保留。 但是,可以将应用设计为在重启设备时自动重新计划通知。 有关详细信息,请参阅 developer.android.com 上的安排重复警报中的当设备重新启动时启动警报。 有关 Android 上后台处理的信息,请参阅 developer.android.com 上的后台处理指南

    处理传入通知

    Android 应用必须检测传入通知并通知 NotificationManagerService 实例。 实现此目标的一种方法是修改 MainActivity 类。

    MainActivity 类上的 Activity 属性应指定 LaunchMode.SingleTopLaunchMode 值:

    [Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, LaunchMode = LaunchMode.SingleTop, //... )]
    public class MainActivity : MauiAppCompatActivity
    

    SingleTop 模式可防止在应用处于前台时启动 Activity 的多个实例。 此 LaunchMode 可能不适合在更复杂的通知方案中启动了多个活动的应用。 有关 LaunchMode 枚举值的详细信息,请参阅 developer.android.com 上的 Android Activity LaunchMode

    还需要修改 MainActivity 类才能接收传入通知:

    public class MainActivity : MauiAppCompatActivity
        protected override void OnCreate(Bundle? savedInstanceState)
            base.OnCreate(savedInstanceState);
            CreateNotificationFromIntent(Intent);
        protected override void OnNewIntent(Intent? intent)
            base.OnNewIntent(intent);
            CreateNotificationFromIntent(intent);
        static void CreateNotificationFromIntent(Intent intent)
            if (intent?.Extras != null)
                string title = intent.GetStringExtra(LocalNotificationsDemo.Platforms.Android.NotificationManagerService.TitleKey);
                string message = intent.GetStringExtra(LocalNotificationsDemo.Platforms.Android.NotificationManagerService.MessageKey);
                var service = IPlatformApplication.Current.Services.GetService<INotificationManagerService>();
                service.ReceiveNotification(title, message);
    

    CreateNotificationFromIntent 方法会从 intent 参数中提取通知数据,并将其传递给 NotificationManagerService 类中的 ReceiveNotification 方法。 CreateNotificationFromIntent 方法是从 OnCreate 方法和 OnNewIntent 方法中调用的:

  • 当应用通过通知数据启动时,会向 OnCreate 方法传递 Intent 数据。
  • 如果应用已在前台,则 Intent 数据将传递给 OnNewIntent 方法。
  • Android 13 (API 33) 及更高版本需要 POST_NOTIFICATIONS 权限才能从应用发送通知。 应在 AndroidManifest.xml 文件中声明此权限:

    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
    

    有关 POST_NOTIFICATIONS 权限的详细信息,请参阅 developer.android.com 上的通知运行时权限

    应实现在运行时检查权限声明的权限类:

    using Android;
    namespace LocalNotificationsDemo.Platforms.Android;
    public class NotificationPermission : Permissions.BasePlatformPermission
        public override (string androidPermission, bool isRuntime)[] RequiredPermissions
                var result = new List<(string androidPermission, bool isRuntime)>();
                if (OperatingSystem.IsAndroidVersionAtLeast(33))
                    result.Add((Manifest.Permission.PostNotifications, true));
                return result.ToArray();
    

    应用启动后,应在尝试发送本地通知之前请求用户授予此权限:

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

    有关 .NET MAUI 权限的详细信息,请参阅“权限”。

    在 iOS 和 Mac Catalyst 上实现本地通知

    在 Apple 平台上,本地通知是向用户传达重要信息的消息。 系统会根据指定的时间或位置处理通知的传递。 有关 Apple 平台上的本地通知的信息,请参阅 developer.apple.com 上的从应用进行本地通知计划

    若要使 .NET MAUI 应用在 Apple 平台上发送和接收通知,应用必须提供 INotificationManagerService 接口的实现。

    发送和接收本地通知

    在 Apple 平台上,NotificationManagerService 类会实现 INotificationManagerService 接口,并包含用于发送和接收本地通知的逻辑:

    using Foundation;
    using UserNotifications;
    namespace LocalNotificationsDemo.Platforms.iOS;
    public class NotificationManagerService : INotificationManagerService
        int messageId = 0;
        bool hasNotificationsPermission;
        public event EventHandler? NotificationReceived;
        public NotificationManagerService()
            // Create a UNUserNotificationCenterDelegate to handle incoming messages.
            UNUserNotificationCenter.Current.Delegate = new NotificationReceiver();
            // Request permission to use local notifications.
            UNUserNotificationCenter.Current.RequestAuthorization(UNAuthorizationOptions.Alert, (approved, err) =>
                hasNotificationsPermission = approved;
        public void SendNotification(string title, string message, DateTime? notifyTime = null)
            // App doesn't have permissions.
            if (!hasNotificationsPermission)
                return;
            messageId++;
            var content = new UNMutableNotificationContent()
                Title = title,
                Subtitle = "",
                Body = message,
                Badge = 1
            UNNotificationTrigger trigger;
            if (notifyTime != null)
                // Create a calendar-based trigger.
                trigger = UNCalendarNotificationTrigger.CreateTrigger(GetNSDateComponents(notifyTime.Value), false);
                // Create a time-based trigger, interval is in seconds and must be greater than 0.
                trigger = UNTimeIntervalNotificationTrigger.CreateTrigger(0.25, false);
            var request = UNNotificationRequest.FromIdentifier(messageId.ToString(), content, trigger);
            UNUserNotificationCenter.Current.AddNotificationRequest(request, (err) =>
                if (err != null)
                    throw new Exception($"Failed to schedule notification: {err}");
        public void ReceiveNotification(string title, string message)
            var args = new NotificationEventArgs()
                Title = title,
                Message = message
            NotificationReceived?.Invoke(null, args);
        NSDateComponents GetNSDateComponents(DateTime dateTime)
            return new NSDateComponents
                Month = dateTime.Month,
                Day = dateTime.Day,
                Year = dateTime.Year,
                Hour = dateTime.Hour,
                Minute = dateTime.Minute,
                Second = dateTime.Second
    

    NotificationManagerService 类应放置在应用的“平台 > iOS”或“平台 > Mac Catalyst”文件夹中。 或者,可以根据自己的文件名和文件夹条件执行多目标,而不是使用“平台”文件夹。 有关详细信息,请参阅配置多目标

    在 Apple 平台上,在尝试计划通知之前,必须先请求使用通知的权限。 这发生在 NotificationManagerService 构造函数中。 有关本地通知权限的详细信息,请参阅 developer.apple.com 上的“请求使用通知的权限”。

    SendNotification 方法定义了创建和发送通知所需的逻辑,并使用 UNTimeIntervalNotificationTrigger 对象或使用 UNCalendarNotificationTrigger 对象的确切 DateTime 来创建即时本地通知。 当收到消息并调用 NotificationReceived 事件处理程序时,iOS 将调用 ReceiveNotification 方法。

    处理传入通知

    在 Apple 平台上,若要处理传入的消息,就必须创建子类化了 UNUserNotificationCenterDelegate 的委托:

    using UserNotifications;
    namespace LocalNotificationsDemo.Platforms.iOS;
    public class NotificationReceiver : UNUserNotificationCenterDelegate
        // Called if app is in the foreground.
        public override void WillPresentNotification(UNUserNotificationCenter center, UNNotification notification, Action<UNNotificationPresentationOptions> completionHandler)
            ProcessNotification(notification);
            var presentationOptions = (OperatingSystem.IsIOSVersionAtLeast(14))
                ? UNNotificationPresentationOptions.Banner
                : UNNotificationPresentationOptions.Alert;
            completionHandler(presentationOptions);
        // Called if app is in the background, or killed state.
        public override void DidReceiveNotificationResponse(UNUserNotificationCenter center, UNNotificationResponse response, Action completionHandler)
            if (response.IsDefaultAction)
                ProcessNotification(response.Notification);
            completionHandler();
        void ProcessNotification(UNNotification notification)
            string title = notification.Request.Content.Title;
            string message = notification.Request.Content.Body;
            var service = IPlatformApplication.Current?.Services.GetService<INotificationManagerService>();
            service?.ReceiveNotification(title, message);
    

    NotificationReceiver 类在 NotificationManagerService 构造函数中注册为 UNUserNotificationCenter 委托,并向 NotificationManagerService 类中的 ReceiveNotification 方法提供传入通知数据。

    在 Windows 上实现本地通知

    Windows 应用 SDK 中的本地通知是指当用户当前不在你的应用程序中时,你的应用程序可以向其发送的消息。 通知内容显示在屏幕右下角和通知中心的临时窗口中。 本地通知可用于通知用户应用的状态,或提示用户采取措施。

    有关 Windows 上的本地通知的信息,包括打包和解压缩应用的实现详细信息,请参阅“应用通知概述”。

    注册平台实现

    应针对 INotificationManagerService 接口注册每个 NotificationManagerService 实现,以便可以从跨平台代码调用接口公开的操作。 为此,可以在 MauiProgram 类的 CreateMauiApp 方法中向 MauiAppBuilder 对象的 Services 属性注册类型:

    #if ANDROID
                builder.Services.AddTransient<INotificationManagerService, LocalNotificationsDemo.Platforms.Android.NotificationManagerService>();
    #elif IOS
                builder.Services.AddTransient<INotificationManagerService, LocalNotificationsDemo.Platforms.iOS.NotificationManagerService>();
    #elif MACCATALYST
                builder.Services.AddTransient<INotificationManagerService, LocalNotificationsDemo.Platforms.MacCatalyst.NotificationManagerService>();
    #elif WINDOWS
                builder.Services.AddTransient<INotificationManagerService, LocalNotificationsDemo.Platforms.Windows.NotificationManagerService>();          
    #endif
    

    有关 .NET MAUI 中依赖项注入的详细信息,请参阅依赖项注入

    发送和接收本地通知

    可以通过自动依赖项解析或显式依赖项解析来解析 INotificationManagerService 实现。 以下示例演示如何使用显式依赖项解析来解析 INotificationManagerService 实现:

    // Assume the app uses a single window.
    INotificationManagerService notificationManager =
        Application.Current?.Windows[0].Page?.Handler?.MauiContext?.Services.GetService<INotificationManagerService>();
    

    有关解析已注册类型的详细信息,请参阅“解析”。

    解析 INotificationManagerService 实现后,即可调用其操作:

    // Send
    notificationManager.SendNotification();
    // Scheduled send
    notificationManager.SendNotification("Notification title goes here", "Notification messages goes here.", DateTime.Now.AddSeconds(10));
    // Receive
    notificationManager.NotificationReceived += (sender, eventArgs) =>
        var eventData = (NotificationEventArgs)eventArgs;
        MainThread.BeginInvokeOnMainThread(() =>
            // Take required action in the app once the notification has been received.
    

    NotificationReceived 事件处理程序会将其事件参数强制转换为 NotificationEventArgs,该参数定义了 TitleMessage 属性:

    public class NotificationEventArgs : EventArgs
        public string Title { get; set; }
        public string Message { get; set; }
    

    在 Android 上,当发送通知时,它会显示为状态栏中的图标:

    在状态栏上向下轻扫时,通知抽屉将打开:

    点击通知会启动应用。 通知在抽屉中仍会可见,直到应用或用户将其消除。

    在 iOS 上,传入通知会由应用自动接收,无需要求用户输入: