设备重新启动后不显示本地通知



我在使用Xamarin的Android上进行本地通知时遇到问题。每当应用程序处于前台或打开时,都会显示本地通知如果我重新启动,则不会显示通知有一个RECEIVE_BOOT_COMPLETEDBroadcastReceiver,但我不知道它是否在重新启动时启动。

我搞不清楚出了什么问题。我检查了三次所有的东西,感觉少了一些小东西。

编辑:由于这个线程,我想我已经解决了关闭应用程序的通知问题:应用程序关闭时广播接收器不工作。从本质上讲,如果进行调试,应用程序需要在关闭后重新打开,以便调用接收器。

我使用此文档设置本地通知: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);
}
}
}
}

我讨厌从网上删除问题。

收到的启动问题是在启动接收器中使用依赖项注入。异常:You must call Xamarin.Forms.Forms.Init(); prior to using this property.我只是实例化了这个类,而没有为这个实例使用DI。

以下是在应用程序关闭时未显示通知的问题上对我有效的方法:https://stackoverflow.com/a/60197247/814891

在manifest,使用JobIntentService应用正确的意向过滤器(并在清单中登记(以处理我的BR打来电话,结果仍然不一致。

一旦我上面列出的所有内容都完成了,你应该能够发送ADB命令以激活广播并在中处理工作即使应用程序已关闭,也可以使用您的服务。这对我有用时间,但不是所有的时间。

本文描述了对BR的限制"从Android 3.1开始安卓系统默认情况下将所有接收者排除在接收意图之外如果用户从未启动过相应的应用程序,或者如果用户通过安卓菜单明确地停止应用程序;(又名用户执行强制停止(

当我通过调试启动应用程序,然后在我的设备,我的ADB命令从未激活我的BR。但是调试会话结束,当我打开设备上的应用程序并滑动它关闭,我可以通过ADB命令激活我的BR。

出现这种情况是因为当您调试应用程序时,然后手动滑动它在设备上关闭,安卓认为这是一个强制停止,因此在重新打开设备上的应用程序之前,无法激活我的BR而无需调试。

在互联网上搜索了几个小时,却找不到我的解决方案,所以我想把它贴在这里,以防某个可怜的不幸灵魂遇到了和我一样奇怪的功能。

快乐编码:(

最新更新