设备重启后本地通知不显示
我在使用 Xamarin 处理 Android 上的本地通知时遇到问题。每次应用程序位于前台或打开时,都会显示本地通知。 如果我重新启动,则不会显示通知。有一个RECEIVE_BOOT_COMPLETED
BroadcastReceiver
,但我不知道它是否在重新启动时触发。
我不知道出了什么问题。我已经检查了三次所有的东西,感觉好像缺少一些小东西。
编辑:我想我已经解决了关于关闭应用程序的通知的问题,这要归功于这个线程: 应用程序关闭时广播接收器不工作。本质上,如果进行调试,应用程序需要在关闭后重新打开,以便调用接收器。
我使用此文档设置本地通知: https ://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/local-notifications
AndroidManifest.cs
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="5" android:versionName="1.0.2.1" package="com.business.myapp" android:installLocation="preferExternal">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
<application android:label="MyApp" android:theme="@style/MainTheme" android:supportsRtl="true" android:icon="@mipmap/icon"></application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
</manifest>
AlarmHandler.cs
using Android.Content;
using MyApp.Business.Services;
using MyApp.Global;
using MyApp.Utils;
using Serilog;
using System;
namespace MyApp.Droid
{
/// <summary>
/// Handle notification receiving.
///
/// https://github.com/xamarin/xamarin-forms-samples/blob/main/LocalNotifications/LocalNotifications/LocalNotifications.Android/AlarmHandler.cs
/// </summary>
[BroadcastReceiver(
Enabled = true,
Exported = true,
Label = "Local Notifications Broadcast Receiver")]
public class AlarmHandler : BroadcastReceiver
{
/// <summary>
/// Called when a notification is shown.
/// </summary>
/// <param name="context"></param>
/// <param name="intent"></param>
public override void OnReceive(Context context, Intent intent)
{
// if (intent?.Extras != null)
// {
if (!Settings.DailyNotificationEnabled) return;
// do not show notification if it is not time based on the settings time value.
// this handles cases where the user changes the notification time multiple times.
/* Disabled on 3/6/2022 to see if this helps notifications.
var timeSpan = DateTime.Now - NotificationDateTime.GetDailyNotificationDateTime();
var absTotalSeconds = Math.Abs(timeSpan.TotalSeconds);
if (absTotalSeconds > 59 && absTotalSeconds < 86399) return;
*/
string title = intent.GetStringExtra(AndroidNotificationManager.TitleKey);
string message = intent.GetStringExtra(AndroidNotificationManager.MessageKey);
title = Constants.NotificationOffllineTitle;
message = Constants.NotificationOffllineMessage;
try
{
var votdHttpRequest = new VotdHttpRequest();
var votdApiDto = votdHttpRequest.MakeApiCall(DateTime.Now, Settings.TwoLetterISOLanguageName);
title = $"{DateTime.Now.ToShortDateString()} {votdApiDto.@ref} ({votdApiDto.ver.ToUpper()})";
var scripture = Settings.DisplayKjvVersion ? votdApiDto.en_scrip_kjv : votdApiDto.en_scrip;
message = Remove.RecursiveHtmlDecode(scripture);
}
catch (Exception ex)
{
Log.Error(ex, "Failed recieving notification.");
}
AndroidNotificationManager manager = AndroidNotificationManager.Instance ?? new AndroidNotificationManager();
manager.Show(title, message);
// }
}
}
}
AndroidNotificationManager.cs
using System;
using Android.App;
using Android.Content;
using Android.Graphics;
using Android.OS;
using AndroidX.Core.App;
using MyApp.Notifications;
using Xamarin.Forms;
using AndroidApp = Android.App.Application;
[assembly: Dependency(typeof(MyApp.Droid.AndroidNotificationManager))]
namespace MyApp.Droid
{
/// <summary>
/// Daily notification manager.
/// </summary>
public class AndroidNotificationManager : INotificationManager
{
#region Private Fields
const string channelId = "default";
const string channelName = "Default";
const string channelDescription = "The default channel for notifications.";
bool channelInitialized = false;
int messageId = 0;
int pendingIntentId = 0;
NotificationManager manager;
#endregion
#region Public Properties
public const string TitleKey = "title";
public const string MessageKey = "message";
public event EventHandler NotificationReceived;
public static AndroidNotificationManager Instance { get; private set; }
public AndroidNotificationManager() => Initialize();
#endregion
#region Constructor
#endregion
#region Private Methods
/// <summary>
/// Create the notification channel.
/// </summary>
void CreateNotificationChannel()
{
manager = (NotificationManager)AndroidApp.Context.GetSystemService(AndroidApp.NotificationService);
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
var channelNameJava = new Java.Lang.String(channelName);
var channel = new NotificationChannel(channelId, channelNameJava, NotificationImportance.Default)
{
Description = channelDescription
};
manager.CreateNotificationChannel(channel);
}
channelInitialized = true;
}
/// <summary>
/// Get the time of notification in UTC ticks.
/// </summary>
/// <param name="notifyTime"></param>
/// <returns></returns>
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
}
#endregion
#region Public Methods
/// <summary>
/// Initialize the notification channel
/// </summary>
public void Initialize()
{
if (Instance == null)
{
CreateNotificationChannel();
Instance = this;
}
}
/// <summary>
/// Schedule a notification.
/// </summary>
/// <param name="title"></param>
/// <param name="message"></param>
/// <param name="notifyTime">Time of notification.</param>
public void SendNotification(string title, string message, int id, DateTime? notifyTime = null)
{
if (!channelInitialized)
{
CreateNotificationChannel();
}
if (notifyTime != null)
{
var intent = CreateIntent(id);
intent.PutExtra(TitleKey, title);
intent.PutExtra(MessageKey, message);
intent.SetIdentifier(id.ToString());
PendingIntent pendingIntent = PendingIntent.GetBroadcast(
AndroidApp.Context,
pendingIntentId++,
intent,
PendingIntentFlags.Immutable);
long triggerTime = GetNotifyTime(notifyTime.Value);
long millisecondsRepeating = AlarmManager.IntervalDay;
AlarmManager alarmManager = AndroidApp.Context.GetSystemService(Context.AlarmService) as AlarmManager;
alarmManager.SetRepeating(AlarmType.RtcWakeup, triggerTime, millisecondsRepeating, pendingIntent);
}
else
{
Show(title, message);
}
}
/// <summary>
/// Called when a notifiation is received.
/// </summary>
/// <param name="title"></param>
/// <param name="message"></param>
public void ReceiveNotification(string title, string message)
{
var args = new NotificationEventArgs()
{
Title = title,
Message = message,
};
NotificationReceived?.Invoke(null, args);
}
/// <summary>
/// Display the notification in the notification area.
/// </summary>
/// <param name="title"></param>
/// <param name="message"></param>
public void Show(string title, string message)
{
Intent intent = new Intent(AndroidApp.Context, typeof(MainActivity));
intent.PutExtra(TitleKey, title);
intent.PutExtra(MessageKey, message);
PendingIntent pendingIntent = PendingIntent.GetActivity(
AndroidApp.Context,
pendingIntentId++,
intent,
PendingIntentFlags.UpdateCurrent | PendingIntentFlags.Immutable);
var textStyle = new NotificationCompat.BigTextStyle();
textStyle.BigText(message);
textStyle.SetSummaryText(title);
NotificationCompat.Builder builder = new NotificationCompat.Builder(AndroidApp.Context, channelId)
.SetContentIntent(pendingIntent)
// .SetContentTitle(title) // not required with big text style
// .SetContentText(message) // not required with big text style
.SetLargeIcon(BitmapFactory.DecodeResource(AndroidApp.Context.Resources, Resource.Drawable.icon_votd))
.SetSmallIcon(Resource.Drawable.icon_votd)
.SetDefaults((int)NotificationDefaults.Sound | (int)NotificationDefaults.Vibrate)
.SetStyle(textStyle);
if (Build.VERSION.SdkInt >= BuildVersionCodes.Lollipop)
builder.SetVisibility((int)NotificationVisibility.Public);
Notification notification = builder.Build();
notification.Flags = NotificationFlags.AutoCancel;
manager.Notify(messageId++, notification);
}
/// <summary>
/// Delete a notification.
/// </summary>
/// <param name="id"></param>
public void DeleteNotification(int id)
{
Intent intent = new Intent(AndroidApp.Context, typeof(AlarmHandler));
PendingIntent pendingIntent = PendingIntent.GetBroadcast(
AndroidApp.Context,
id, intent,
PendingIntentFlags.CancelCurrent | PendingIntentFlags.Immutable);
AlarmManager alarmManager = AndroidApp.Context.GetSystemService(Context.AlarmService) as AlarmManager;
alarmManager.Cancel(pendingIntent);
}
private Intent CreateIntent(int id)
{
return new Intent(Android.App.Application.Context, typeof(AlarmHandler)).SetAction("LocalNotifierIntent" + id);
}
public void CancelAlarm(int id)
{
var intent = CreateIntent(id);
var pendingIntent = PendingIntent.GetBroadcast(
Android.App.Application.Context,
0,
intent,
PendingIntentFlags.UpdateCurrent | PendingIntentFlags.Immutable);
var alarmManager = GetAlarmManager();
alarmManager.Cancel(pendingIntent);
var notificationManager = NotificationManagerCompat.From(Android.App.Application.Context);
notificationManager.CancelAll();
notificationManager.Cancel(id);
}
private AlarmManager GetAlarmManager()
{
var alarmManager = Android.App.Application.Context.GetSystemService(Context.AlarmService) as AlarmManager;
return alarmManager;
}
#endregion
}
}
BootReciever.cs
using Android.App;
using Android.Content;
using MyApp.Notifications;
using MyApp.Utils;
using System;
using Xamarin.Forms;
namespace MyApp.Droid.Notifications
{
/// <summary>
/// Used to reschedule notifications on boot. Android notifications are deleted when the OS is rebooted.
/// </summary>
[BroadcastReceiver(
Enabled = true,
Label = "Reboot complete receiver",
Exported = true)]
[IntentFilter(new[] { Android.Content.Intent.ActionBootCompleted })]
public class BootReceiver : BroadcastReceiver
{
/// <summary>
/// Called on boot.
/// </summary>
/// <param name="context"></param>
/// <param name="intent"></param>
public override void OnReceive(Context context, Intent intent)
{
//if (intent.Action == "android.intent.action.BOOT_COMPLETED")
//{
// ScheduleDailyNotification();
//}
ScheduleDailyNotification();
}
/// <summary>
/// Schedule the daily notification.
/// </summary>
private void ScheduleDailyNotification()
{
if (Settings.DailyNotificationEnabled)
{
var notificationManager = DependencyService.Get<INotificationManager>();
var notificationDateTime = NotificationDateTime.GetDailyNotificationDateTime();
notificationManager.SendNotification(
Global.Constants.NotificationOffllineTitle,
Global.Constants.NotificationOffllineMessage,
Global.Constants.DailyNotificationId,
//DateTime.Now.AddSeconds(60));
notificationDateTime);
}
}
}
}
I am having trouble with local notification on Android using Xamarin. The local notification shows every time the app is in the foreground or open. If I reboot the notification does not show. There is a RECEIVE_BOOT_COMPLETED
BroadcastReceiver
but I do not know if it is firing on reboot.
I cannot figure out what is wrong. I have tripled checked everything and feel like something small is missing.
Edit: I think I have it worked out for notifications on the app being closed thanks to this thread: Broadcast receiver not working when app is closed. Essentially, if debugging, the app needs to be reopened after closing it so that the receiver is called.
I used this doc to setup local notifications: https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/local-notifications
AndroidManifest.cs
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="5" android:versionName="1.0.2.1" package="com.business.myapp" android:installLocation="preferExternal">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
<application android:label="MyApp" android:theme="@style/MainTheme" android:supportsRtl="true" android:icon="@mipmap/icon"></application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
</manifest>
AlarmHandler.cs
using Android.Content;
using MyApp.Business.Services;
using MyApp.Global;
using MyApp.Utils;
using Serilog;
using System;
namespace MyApp.Droid
{
/// <summary>
/// Handle notification receiving.
///
/// https://github.com/xamarin/xamarin-forms-samples/blob/main/LocalNotifications/LocalNotifications/LocalNotifications.Android/AlarmHandler.cs
/// </summary>
[BroadcastReceiver(
Enabled = true,
Exported = true,
Label = "Local Notifications Broadcast Receiver")]
public class AlarmHandler : BroadcastReceiver
{
/// <summary>
/// Called when a notification is shown.
/// </summary>
/// <param name="context"></param>
/// <param name="intent"></param>
public override void OnReceive(Context context, Intent intent)
{
// if (intent?.Extras != null)
// {
if (!Settings.DailyNotificationEnabled) return;
// do not show notification if it is not time based on the settings time value.
// this handles cases where the user changes the notification time multiple times.
/* Disabled on 3/6/2022 to see if this helps notifications.
var timeSpan = DateTime.Now - NotificationDateTime.GetDailyNotificationDateTime();
var absTotalSeconds = Math.Abs(timeSpan.TotalSeconds);
if (absTotalSeconds > 59 && absTotalSeconds < 86399) return;
*/
string title = intent.GetStringExtra(AndroidNotificationManager.TitleKey);
string message = intent.GetStringExtra(AndroidNotificationManager.MessageKey);
title = Constants.NotificationOffllineTitle;
message = Constants.NotificationOffllineMessage;
try
{
var votdHttpRequest = new VotdHttpRequest();
var votdApiDto = votdHttpRequest.MakeApiCall(DateTime.Now, Settings.TwoLetterISOLanguageName);
title = quot;{DateTime.Now.ToShortDateString()} {votdApiDto.@ref} ({votdApiDto.ver.ToUpper()})";
var scripture = Settings.DisplayKjvVersion ? votdApiDto.en_scrip_kjv : votdApiDto.en_scrip;
message = Remove.RecursiveHtmlDecode(scripture);
}
catch (Exception ex)
{
Log.Error(ex, "Failed recieving notification.");
}
AndroidNotificationManager manager = AndroidNotificationManager.Instance ?? new AndroidNotificationManager();
manager.Show(title, message);
// }
}
}
}
AndroidNotificationManager.cs
using System;
using Android.App;
using Android.Content;
using Android.Graphics;
using Android.OS;
using AndroidX.Core.App;
using MyApp.Notifications;
using Xamarin.Forms;
using AndroidApp = Android.App.Application;
[assembly: Dependency(typeof(MyApp.Droid.AndroidNotificationManager))]
namespace MyApp.Droid
{
/// <summary>
/// Daily notification manager.
/// </summary>
public class AndroidNotificationManager : INotificationManager
{
#region Private Fields
const string channelId = "default";
const string channelName = "Default";
const string channelDescription = "The default channel for notifications.";
bool channelInitialized = false;
int messageId = 0;
int pendingIntentId = 0;
NotificationManager manager;
#endregion
#region Public Properties
public const string TitleKey = "title";
public const string MessageKey = "message";
public event EventHandler NotificationReceived;
public static AndroidNotificationManager Instance { get; private set; }
public AndroidNotificationManager() => Initialize();
#endregion
#region Constructor
#endregion
#region Private Methods
/// <summary>
/// Create the notification channel.
/// </summary>
void CreateNotificationChannel()
{
manager = (NotificationManager)AndroidApp.Context.GetSystemService(AndroidApp.NotificationService);
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
var channelNameJava = new Java.Lang.String(channelName);
var channel = new NotificationChannel(channelId, channelNameJava, NotificationImportance.Default)
{
Description = channelDescription
};
manager.CreateNotificationChannel(channel);
}
channelInitialized = true;
}
/// <summary>
/// Get the time of notification in UTC ticks.
/// </summary>
/// <param name="notifyTime"></param>
/// <returns></returns>
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
}
#endregion
#region Public Methods
/// <summary>
/// Initialize the notification channel
/// </summary>
public void Initialize()
{
if (Instance == null)
{
CreateNotificationChannel();
Instance = this;
}
}
/// <summary>
/// Schedule a notification.
/// </summary>
/// <param name="title"></param>
/// <param name="message"></param>
/// <param name="notifyTime">Time of notification.</param>
public void SendNotification(string title, string message, int id, DateTime? notifyTime = null)
{
if (!channelInitialized)
{
CreateNotificationChannel();
}
if (notifyTime != null)
{
var intent = CreateIntent(id);
intent.PutExtra(TitleKey, title);
intent.PutExtra(MessageKey, message);
intent.SetIdentifier(id.ToString());
PendingIntent pendingIntent = PendingIntent.GetBroadcast(
AndroidApp.Context,
pendingIntentId++,
intent,
PendingIntentFlags.Immutable);
long triggerTime = GetNotifyTime(notifyTime.Value);
long millisecondsRepeating = AlarmManager.IntervalDay;
AlarmManager alarmManager = AndroidApp.Context.GetSystemService(Context.AlarmService) as AlarmManager;
alarmManager.SetRepeating(AlarmType.RtcWakeup, triggerTime, millisecondsRepeating, pendingIntent);
}
else
{
Show(title, message);
}
}
/// <summary>
/// Called when a notifiation is received.
/// </summary>
/// <param name="title"></param>
/// <param name="message"></param>
public void ReceiveNotification(string title, string message)
{
var args = new NotificationEventArgs()
{
Title = title,
Message = message,
};
NotificationReceived?.Invoke(null, args);
}
/// <summary>
/// Display the notification in the notification area.
/// </summary>
/// <param name="title"></param>
/// <param name="message"></param>
public void Show(string title, string message)
{
Intent intent = new Intent(AndroidApp.Context, typeof(MainActivity));
intent.PutExtra(TitleKey, title);
intent.PutExtra(MessageKey, message);
PendingIntent pendingIntent = PendingIntent.GetActivity(
AndroidApp.Context,
pendingIntentId++,
intent,
PendingIntentFlags.UpdateCurrent | PendingIntentFlags.Immutable);
var textStyle = new NotificationCompat.BigTextStyle();
textStyle.BigText(message);
textStyle.SetSummaryText(title);
NotificationCompat.Builder builder = new NotificationCompat.Builder(AndroidApp.Context, channelId)
.SetContentIntent(pendingIntent)
// .SetContentTitle(title) // not required with big text style
// .SetContentText(message) // not required with big text style
.SetLargeIcon(BitmapFactory.DecodeResource(AndroidApp.Context.Resources, Resource.Drawable.icon_votd))
.SetSmallIcon(Resource.Drawable.icon_votd)
.SetDefaults((int)NotificationDefaults.Sound | (int)NotificationDefaults.Vibrate)
.SetStyle(textStyle);
if (Build.VERSION.SdkInt >= BuildVersionCodes.Lollipop)
builder.SetVisibility((int)NotificationVisibility.Public);
Notification notification = builder.Build();
notification.Flags = NotificationFlags.AutoCancel;
manager.Notify(messageId++, notification);
}
/// <summary>
/// Delete a notification.
/// </summary>
/// <param name="id"></param>
public void DeleteNotification(int id)
{
Intent intent = new Intent(AndroidApp.Context, typeof(AlarmHandler));
PendingIntent pendingIntent = PendingIntent.GetBroadcast(
AndroidApp.Context,
id, intent,
PendingIntentFlags.CancelCurrent | PendingIntentFlags.Immutable);
AlarmManager alarmManager = AndroidApp.Context.GetSystemService(Context.AlarmService) as AlarmManager;
alarmManager.Cancel(pendingIntent);
}
private Intent CreateIntent(int id)
{
return new Intent(Android.App.Application.Context, typeof(AlarmHandler)).SetAction("LocalNotifierIntent" + id);
}
public void CancelAlarm(int id)
{
var intent = CreateIntent(id);
var pendingIntent = PendingIntent.GetBroadcast(
Android.App.Application.Context,
0,
intent,
PendingIntentFlags.UpdateCurrent | PendingIntentFlags.Immutable);
var alarmManager = GetAlarmManager();
alarmManager.Cancel(pendingIntent);
var notificationManager = NotificationManagerCompat.From(Android.App.Application.Context);
notificationManager.CancelAll();
notificationManager.Cancel(id);
}
private AlarmManager GetAlarmManager()
{
var alarmManager = Android.App.Application.Context.GetSystemService(Context.AlarmService) as AlarmManager;
return alarmManager;
}
#endregion
}
}
BootReciever.cs
using Android.App;
using Android.Content;
using MyApp.Notifications;
using MyApp.Utils;
using System;
using Xamarin.Forms;
namespace MyApp.Droid.Notifications
{
/// <summary>
/// Used to reschedule notifications on boot. Android notifications are deleted when the OS is rebooted.
/// </summary>
[BroadcastReceiver(
Enabled = true,
Label = "Reboot complete receiver",
Exported = true)]
[IntentFilter(new[] { Android.Content.Intent.ActionBootCompleted })]
public class BootReceiver : BroadcastReceiver
{
/// <summary>
/// Called on boot.
/// </summary>
/// <param name="context"></param>
/// <param name="intent"></param>
public override void OnReceive(Context context, Intent intent)
{
//if (intent.Action == "android.intent.action.BOOT_COMPLETED")
//{
// ScheduleDailyNotification();
//}
ScheduleDailyNotification();
}
/// <summary>
/// Schedule the daily notification.
/// </summary>
private void ScheduleDailyNotification()
{
if (Settings.DailyNotificationEnabled)
{
var notificationManager = DependencyService.Get<INotificationManager>();
var notificationDateTime = NotificationDateTime.GetDailyNotificationDateTime();
notificationManager.SendNotification(
Global.Constants.NotificationOffllineTitle,
Global.Constants.NotificationOffllineMessage,
Global.Constants.DailyNotificationId,
//DateTime.Now.AddSeconds(60));
notificationDateTime);
}
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我讨厌从互联网上删除问题。
收到的启动问题是在启动接收器中使用依赖项注入。例外:
您必须调用 Xamarin.Forms.Forms.Init();在使用此属性之前。
我只是实例化了该类,而不是为此实例使用 DI。以下是针对应用程序关闭时未显示通知的问题对我有用的方法: https://stackoverflow.com/a/ 60197247/814891
I hate to delete a question from the internet.
The issue on boot received was using dependency injection in the boot receiver. Exception:
You must call Xamarin.Forms.Forms.Init(); prior to using this property.
I just instantiated the class instead of using DI for this instance.Here is what worked for me on the issue with the notification not showing when the app was closed: https://stackoverflow.com/a/60197247/814891