Xamarin.Forms定时本地通知不显示且无报错问题求助
排查Xamarin.Forms定时本地通知无法显示的问题
我按照教程实现了Xamarin.Forms定时重复本地通知,但通知完全不显示,也没有任何错误或异常抛出。以下是全部相关代码,请帮忙定位问题:
Android平台实现代码(LocalNotification.cs)
using System; using System.IO; using System.Xml.Serialization; using Android.App; using Android.Content; using Android.Media; using AndroidX.Core.App; using Java.Lang; using Test.Droid; using Test.Models; using Test.Views; [assembly: Xamarin.Forms.Dependency(typeof(LocalNotificationService))] namespace Test.Droid { public class LocalNotificationService : ILocalNotificationService { int _notificationIconId { get; set; } readonly DateTime _jan1st1970 = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); internal string _randomNumber; public void LocalNotification(string title, string body, int id, DateTime notifyTime) { //long repeateDay = 1000 * 60 * 60 * 24; long repeateForMinute = 60000; // In milliseconds long totalMilliSeconds = (long)(notifyTime.ToUniversalTime() - _jan1st1970).TotalMilliseconds; if (totalMilliSeconds < JavaSystem.CurrentTimeMillis()) { totalMilliSeconds = totalMilliSeconds + repeateForMinute; } var intent = CreateIntent(id); var localNotification = new LocalNotification(); localNotification.Title = title; localNotification.Body = body; localNotification.Id = id; localNotification.NotifyTime = notifyTime; if (_notificationIconId != 0) { localNotification.IconId = _notificationIconId; } else { localNotification.IconId = Resource.Drawable.icon; } var serializedNotification = SerializeNotification(localNotification); intent.PutExtra(ScheduledAlarmHandler.LocalNotificationKey, serializedNotification); Random generator = new Random(); _randomNumber = generator.Next(100000, 999999).ToString("D6"); var pendingIntent = PendingIntent.GetBroadcast(Application.Context, Convert.ToInt32(_randomNumber), intent, PendingIntentFlags.Immutable); var alarmManager = GetAlarmManager(); alarmManager.SetRepeating(AlarmType.RtcWakeup, totalMilliSeconds, repeateForMinute, pendingIntent); } public void Cancel(int id) { var intent = CreateIntent(id); var pendingIntent = PendingIntent.GetBroadcast(Application.Context, Convert.ToInt32(_randomNumber), intent, PendingIntentFlags.Immutable); var alarmManager = GetAlarmManager(); alarmManager.Cancel(pendingIntent); var notificationManager = NotificationManagerCompat.From(Application.Context); notificationManager.CancelAll(); notificationManager.Cancel(id); } public static Intent GetLauncherActivity() { var packageName = Application.Context.PackageName; return Application.Context.PackageManager.GetLaunchIntentForPackage(packageName); } private Intent CreateIntent(int id) { return new Intent(Application.Context, typeof(ScheduledAlarmHandler)) .SetAction("LocalNotifierIntent" + id); } private AlarmManager GetAlarmManager() { var alarmManager = Application.Context.GetSystemService(Context.AlarmService) as AlarmManager; return alarmManager; } private string SerializeNotification(LocalNotification notification) { var xmlSerializer = new XmlSerializer(notification.GetType()); using (var stringWriter = new StringWriter()) { xmlSerializer.Serialize(stringWriter, notification); return stringWriter.ToString(); } } } [BroadcastReceiver(Enabled = true, Label = "Local Notifications Broadcast Receiver")] public class ScheduledAlarmHandler : BroadcastReceiver { public const string LocalNotificationKey = "LocalNotification"; [Obsolete] public override void OnReceive(Context context, Intent intent) { var extra = intent.GetStringExtra(LocalNotificationKey); var notification = DeserializeNotification(extra); //Generating notification var builder = new NotificationCompat.Builder(Application.Context) .SetContentTitle(notification.Title) .SetContentText(notification.Body) .SetSmallIcon(notification.IconId) .SetSound(RingtoneManager.GetDefaultUri(RingtoneType.Ringtone)) .SetAutoCancel(true); var resultIntent = LocalNotificationService.GetLauncherActivity(); resultIntent.SetFlags(ActivityFlags.NewTask | ActivityFlags.ClearTask); var stackBuilder = AndroidX.Core.App.TaskStackBuilder.Create(Android.App.Application.Context); stackBuilder.AddNextIntent(resultIntent); Random random = new Random(); int randomNumber = random.Next(9999 - 1000) + 1000; var resultPendingIntent = stackBuilder.GetPendingIntent(randomNumber, (int)PendingIntentFlags.Immutable); builder.SetContentIntent(resultPendingIntent); // Sending notification var notificationManager = NotificationManagerCompat.From(Application.Context); notificationManager.Notify(randomNumber, builder.Build()); } private LocalNotification DeserializeNotification(string notificationString) { var xmlSerializer = new XmlSerializer(typeof(LocalNotification)); using (var stringReader = new StringReader(notificationString)) { var notification = (LocalNotification)xmlSerializer.Deserialize(stringReader); return notification; } } } }
XAML页面(LocalNotification.xaml)
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Test.Views.LocalNotificationPage" BackgroundColor="#533F95"> <ContentPage.Content> <Grid VerticalOptions="FillAndExpand" Padding="25,40,25,30" RowSpacing="20"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <Grid Grid.Row="0"> <StackLayout HorizontalOptions="FillAndExpand" Orientation="Horizontal"> <Label Text="Notifications ON/OFF" TextColor="White" FontSize="16" HorizontalOptions="StartAndExpand" VerticalOptions="Center"/> <Switch IsToggled="{Binding NotificationONOFF}" HorizontalOptions="EndAndExpand" VerticalOptions="Center"/> </StackLayout> </Grid> <Grid Grid.Row="1"> <StackLayout HorizontalOptions="FillAndExpand" Orientation="Horizontal"> <Label Text="SET TIME" HorizontalOptions="StartAndExpand" FontSize="15" TextColor="White" VerticalOptions="Center"/> <TimePicker HorizontalOptions="EndAndExpand" Time="{Binding SelectedTime}" TextColor="White" BackgroundColor="Transparent" Format="t"/> </StackLayout> </Grid> <Grid Grid.Row="2"> <StackLayout HorizontalOptions="FillAndExpand" Orientation="Horizontal"> <Label Text="SET DATE" TextColor="White" FontSize="15" VerticalOptions="Center" HorizontalOptions="StartAndExpand"/> <DatePicker HorizontalOptions="EndAndExpand" Date="{Binding SelectedDate}" TextColor="White" BackgroundColor="Transparent" Format="MM-dd-yyyy"/> </StackLayout> </Grid> <Grid Grid.Row="3"> <StackLayout HorizontalOptions="FillAndExpand" Spacing="10"> <Label Text="Enter Message" FontSize="15" HorizontalOptions="StartAndExpand" TextColor="White" VerticalOptions="Center"/> <Editor HeightRequest="120" Text="{Binding MessageText}" TextColor="Purple" BackgroundColor="White" HorizontalOptions="FillAndExpand"/> </StackLayout> </Grid> <Grid Grid.Row="4"> <Button Text="Save" Command="{Binding SaveCommand}" FontSize="15" TextColor="White" BackgroundColor="Purple" HorizontalOptions="FillAndExpand" BorderRadius="15"/> </Grid> </Grid> </ContentPage.Content> </ContentPage>
页面代码-behind(LocalNotification.xaml.cs)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Xamarin.Forms; using Xamarin.Forms.Xaml; namespace Test.Views { public partial class LocalNotificationPage : ContentPage { public LocalNotificationPage() { InitializeComponent(); BindingContext = new LocalNotificationPageViewModel(); } } }
ViewModel(LocalNotificationviewmode.cs)
using System; using System.Collections.Generic; using System.ComponentModel; using System.Globalization; using System.Runtime.CompilerServices; using System.Text; using Xamarin.Forms; namespace Test.Views { public class LocalNotificationPageViewModel : INotifyPropertyChanged { Command _saveCommand; public Command SaveCommand { get { return _saveCommand; } set { SetProperty(ref _saveCommand, value); } } bool _notificationONOFF; public bool NotificationONOFF { get { return _notificationONOFF; } set { SetProperty(ref _notificationONOFF, value); Switch_Toggled(); } } void Switch_Toggled() { if (NotificationONOFF == false) { MessageText = string.Empty; SelectedTime = DateTime.Now.TimeOfDay; SelectedDate = DateTime.Today; DependencyService.Get<ILocalNotificationService>().Cancel(0); } } DateTime _selectedDate = DateTime.Today; public DateTime SelectedDate { get { return _selectedDate; } set { SetProperty(ref _selectedDate, value); } } TimeSpan _selectedTime = DateTime.Now.TimeOfDay; public TimeSpan SelectedTime { get { return _selectedTime; } set { SetProperty(ref _selectedTime, value); } } string _messageText; public string MessageText { get { return _messageText; } set { SetProperty(ref _messageText, value); } } public LocalNotificationPageViewModel() { SaveCommand = new Command(() => SaveLocalNotification()); } void SaveLocalNotification() { if (NotificationONOFF == true) { var date = (SelectedDate.Date.Month.ToString("00") + "-" + SelectedDate.Date.Day.ToString("00") + "-" + SelectedDate.Date.Year.ToString()); var time = Convert.ToDateTime(SelectedTime.ToString()).ToString("HH:mm"); var dateTime = date + " " + time; var selectedDateTime = DateTime.ParseExact(dateTime, "MM-dd-yyyy HH:mm", CultureInfo.InvariantCulture); if (!string.IsNullOrEmpty(MessageText)) { DependencyService.Get<ILocalNotificationService>().Cancel(0); DependencyService.Get <ILocalNotificationService>().LocalNotification("Local Notification", MessageText, 0, selectedDateTime); App.Current.MainPage.DisplayAlert("LocalNotificationDemo", "Notification details saved successfully ", "Ok"); } else { App.Current.MainPage.DisplayAlert("LocalNotificationDemo", "Please enter meassage", "OK"); } } else { App.Current.MainPage.DisplayAlert("LocalNotificationDemo", "Please switch on notification", "OK"); } } protected bool SetProperty<T>(ref T backingStore, T value, [CallerMemberName] string propertyName = "", Action onChanged = null) { if (EqualityComparer<T>.Default.Equals(backingStore, value)) return false; backingStore = value; onChanged?.Invoke(); OnPropertyChanged(propertyName); return true; } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propertyName = "") { var changed = PropertyChanged; if (changed == null) return; changed.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } }
服务接口(Ilocalnotificationservice.cs)
using System; using System.Collections.Generic; using System.Text; namespace Test.Views { public interface ILocalNotificationService { void LocalNotification(string title, string body, int id, DateTime notifyTime); void Cancel(int id); } }
核心问题排查与修复建议
缺失Android通知渠道(API 26+强制要求)
Android 8.0及以上版本必须创建通知渠道才能显示通知,你的代码完全没有渠道创建逻辑。在ScheduledAlarmHandler的OnReceive方法开头添加以下代码:if (Build.VERSION.SdkInt >= BuildVersionCodes.O) { var channelId = "LocalNotificationChannel"; var channelName = "本地通知"; var channelDescription = "定时重复通知渠道"; var channel = new NotificationChannel(channelId, channelName, NotificationImportance.Default) { Description = channelDescription }; var notificationManager = context.GetSystemService(Context.NotificationService) as NotificationManager; notificationManager.CreateNotificationChannel(channel); builder.SetChannelId(channelId); }PendingIntent请求码逻辑错误
你使用随机数作为PendingIntent的请求码,导致应用重启后Cancel方法无法找到对应的PendingIntent,同时可能覆盖旧的闹钟任务。建议直接使用通知ID作为请求码:// 在LocalNotification方法中 var pendingIntent = PendingIntent.GetBroadcast(Application.Context, id, intent, PendingIntentFlags.Immutable); // 在Cancel方法中 var pendingIntent = PendingIntent.GetBroadcast(Application.Context, id, intent, PendingIntentFlags.Immutable);AlarmManager重复闹钟兼容性问题
Android 6.0+的Doze模式会限制SetRepeating的精准度,若需要可靠的重复通知,建议使用SetExactAndAllowWhileIdle,并在每次触发后重新设置下一次闹钟。图标资源有效性验证
确保Resource.Drawable.icon存在于Android项目中,且符合通知图标规范(透明背景、尺寸合规),无效图标会导致通知无法显示。DateTime转换逻辑简化
ViewModel中拼接日期时间的方式容易出错,可简化为:var selectedDateTime = new DateTime(SelectedDate.Year, SelectedDate.Month, SelectedDate.Day, SelectedTime.Hours, SelectedTime.Minutes, SelectedTime.Seconds);
内容的提问来源于stack exchange,提问作者Vired
相关产品推荐
相关产品推荐

