c# - Android Xamarin - 使用 SQLite 时打开计划通知会停止重复和崩溃
问题描述
我使用 SQLite 创建了一个具有 6 个数据库表的 Xamarin Android 应用程序。我已经实现了一个重复通知功能,每次调用通知时都会显示 SQLite 表中的不同元素。这是通过让应用程序在后台运行并通过 SQL 数据找到要在通知中显示的元素来完成的。如果点击通知,则打开应用程序,通知中显示的信息显示在应用程序的页面上。
所以我能做的就是打开我的应用程序并将其设置为显示重复通知。每次根据通过我的 SQL 数据的后台函数触发通知时,通知会显示不同的信息。我退出我的应用程序并清除我最近的应用程序以使其在后台运行。通知和更改信息效果很好。但是,一旦我重新打开应用程序,通知就会停止触发,稍后当我关闭应用程序时,我会收到应用程序停止响应的通知。
我相信问题来自 Android 警报服务和 SQLite 数据连接。即使在我再次打开应用程序后,如何让我的通知不断重复?如果在最初设置要显示的通知后我再也没有打开应用程序,则通知将继续显示而不会出现错误。
这是我设置警报管理器并创建通知的课程
[assembly: Dependency(typeof(LawsForImpact.Droid.AndroidNotificationManager))]
namespace LawsForImpact.Droid
{
[BroadcastReceiver]
public class AndroidNotificationManager : BroadcastReceiver, INotificationManager
{
const string channelId = "default";
const string channelName = "Default";
const string channelDescription = "The default channel for notifications.";
const int pendingIntentId = 0;
public const string LocalNotificationKey = "LocalNotification";
public const string TableKey = "table";
public const string IndexKey = "index";
bool channelInitialized = false;
int messageId = -1;
NotificationManager manager;
public event EventHandler NotificationReceived;
string currentTitle;
int currentIndex;
SavedInformation savedInfo;
public void Initialize()
{
CreateNotificationChannel();
}
public int ScheduleNotification(string title, string message)
{
if (!channelInitialized)
{
CreateNotificationChannel();
}
messageId++;
// 1 MainActivity intent allows MainActivity to change once notification tapped
Intent intentMain = new Intent(AndroidApp.Context, typeof(MainActivity));
intentMain.PutExtra(TableKey, currentTitle);
intentMain.PutExtra(IndexKey, currentIndex);
PendingIntent pendingIntentMain = PendingIntent.GetActivity(AndroidApp.Context, pendingIntentId, intentMain, PendingIntentFlags.UpdateCurrent);
NotificationCompat.BigTextStyle textStyle = new NotificationCompat.BigTextStyle();
NotificationCompat.Builder builder = new NotificationCompat.Builder(AndroidApp.Context, channelId)
.SetContentIntent(pendingIntentMain)
.SetContentTitle(title)
.SetContentText(message)
.SetLargeIcon(BitmapFactory.DecodeResource(AndroidApp.Context.Resources, Resource.Drawable.ic_mtrl_chip_checked_circle))
.SetSmallIcon(Resource.Drawable.ic_mtrl_chip_checked_black)
.SetDefaults((int)NotificationDefaults.Sound | (int)NotificationDefaults.Vibrate)
.SetStyle(textStyle);
Random random = new Random();
int randomNumber = random.Next(9999 - 1000) + 1000;
Notification notification = builder.Build();
manager.Notify(randomNumber, notification);
return messageId;
}
public void SavedInfo(SerializableDictionary<string, int> pickedQueue, int queueIndex, bool randomTog, int repeatInterval)
{
savedInfo = new SavedInformation();
currentTitle = pickedQueue.ElementAt(queueIndex).Key;
savedInfo.QueueOfSaved = pickedQueue;
savedInfo.QueueIndex = queueIndex;
savedInfo.RandomToggle = randomTog;
savedInfo.RepeatInterval = repeatInterval;
LoadData();
RepeatAlarmSet();
}
public override void OnReceive(Context context, Intent intent)
{
var extra = intent.GetStringExtra(LocalNotificationKey);
var notification = DeserializeNotification(extra);
SerializableDictionary<string, int> queue = notification.QueueOfSaved;
var queueIndex = notification.QueueIndex;
var randTog = notification.RandomToggle;
var repInterval = notification.RepeatInterval;
SavedInfo(queue, queueIndex, randTog, repInterval);
}
public void RepeatAlarmSet()
{
Intent intent = new Intent(Application.Context, typeof(AndroidNotificationManager));
var serializedNotification = SerializeNotification(savedInfo);
intent.PutExtra(LocalNotificationKey, serializedNotification);
var pendingIntent = PendingIntent.GetBroadcast(Application.Context, 0, intent, PendingIntentFlags.UpdateCurrent);
var alarmManager = GetAlarmManager();
alarmManager.SetExactAndAllowWhileIdle(AlarmType.RtcWakeup, 1000, pendingIntent);
}
public void Cancel()
{
var intent = new Intent(Application.Context, typeof(AndroidNotificationManager));
var pendingIntent = PendingIntent.GetBroadcast(Application.Context, 0, intent, PendingIntentFlags.UpdateCurrent);
var alarmManager = GetAlarmManager();
pendingIntent.Cancel();
alarmManager.Cancel(pendingIntent);
var notificationManager = NotificationManagerCompat.From(Application.Context);
notificationManager.CancelAll();
}
private async void LoadData()
{
SQLiteConnection _sqLiteConnection;
_sqLiteConnection = await Xamarin.Forms.DependencyService.Get<ISQLite>().GetConnection();
IEnumerable<IDataTable> tableToEnumerable = new List<IDataTable>();
List<IDataTable> listData;
switch (currentTitle)
{
case "Table1":
tableToEnumerable = _sqLiteConnection.Table<Table1>().ToList();
break;
case "Table2":
tableToEnumerable = _sqLiteConnection.Table<Table2>().ToList();
break;
case "Table3":
tableToEnumerable = _sqLiteConnection.Table<Table3>().ToList();
break;
case "Table4":
tableToEnumerable = _sqLiteConnection.Table<Table4>().ToList();
break;
case "Table5":
tableToEnumerable = _sqLiteConnection.Table<Table5>().ToList();
break;
case "Table6":
tableToEnumerable = _sqLiteConnection.Table<Table6>().ToList();
break;
}
listData = tableToEnumerable.ToList();
int index = listData.Count() - savedInfo.QueueOfSaved[currentTitle];
index = index - 1;
currentIndex = index;
// if random enabled
if (savedInfo.RandomToggle)
{
Random random = new Random();
index = random.Next(0, listData.Count());
}
//sets all the current notification information
string title = listData[index].Title;
string message = listData[index].Description;
//logic for next notification
// subtract the queue int of current notification subject to keep track of next index
savedInfo.QueueOfSaved[currentTitle] = savedInfo.QueueOfSaved[currentTitle] - 1;
// check for index overflow
if (savedInfo.QueueOfSaved[currentTitle] < 0)
{
savedInfo.QueueOfSaved[currentTitle] = listData.Count() - 1;
}
// index of next table
savedInfo.QueueIndex = savedInfo.QueueIndex + 1;
// if next table index overflows that means its time to restart the table index and move up the notification index
if (savedInfo.QueueIndex >= savedInfo.QueueOfSaved.Count)
{
savedInfo.QueueIndex = 0;
}
ScheduleNotification(title, message);
}
public void ReceiveNotification(string table, int index)
{
var args = new NotificationEventArgs()
{
Table = table,
Index = index,
};
NotificationReceived?.Invoke(null, args);
var notificationManager = NotificationManagerCompat.From(Application.Context);
notificationManager.CancelAll();
}
public static Intent GetLauncherActivity()
{
var packageName = Application.Context.PackageName;
return Application.Context.PackageManager.GetLaunchIntentForPackage(packageName);
}
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;
}
private string SerializeNotification(SavedInformation notification)
{
var xmlSerializer = new XmlSerializer(notification.GetType());
using (var stringWriter = new StringWriter())
{
xmlSerializer.Serialize(stringWriter, notification);
return stringWriter.ToString();
}
}
private SavedInformation DeserializeNotification(string notificationString)
{
var xmlSerializer = new XmlSerializer(typeof(SavedInformation));
using (var stringReader = new StringReader(notificationString))
{
var notification = (SavedInformation)xmlSerializer.Deserialize(stringReader);
return notification;
}
}
private AlarmManager GetAlarmManager()
{
var alarmManager = Application.Context.GetSystemService(Context.AlarmService) as AlarmManager;
return alarmManager;
}
}
我相信 MainActivity 以某种方式参与其中,因为它负责重新启动我的应用程序的过程。
[Activity(
LaunchMode = LaunchMode.SingleTop,
Label = "LawsForImpact",
Icon = "@mipmap/icon",
Theme = "@style/MainTheme",
MainLauncher = true,
ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle savedInstanceState)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(new App());
CreateNotificationFromIntent(Intent);
}
protected override void OnNewIntent(Intent intent)
{
CreateNotificationFromIntent(intent);
}
void CreateNotificationFromIntent(Intent intent)
{
if (intent?.Extras != null)
{
string tableKey = intent.Extras.GetString(AndroidNotificationManager.TableKey);
int indexKey = intent.Extras.GetInt(AndroidNotificationManager.IndexKey);
DependencyService.Get<INotificationManager>().ReceiveNotification(tableKey, indexKey);
}
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
解决方案
我在后台进程中加载 SQL 数据时遇到的问题。依赖服务导致了这个问题。我通过使用 Android Device Monitor 工具并借助以下工具解决了这个问题:Xamarin Service and Forms Init
推荐阅读
- javascript - 无法检索 this.lastID
- r - 将带有空格/NA的文本文件导入并解析到R中
- javascript - CodeIgniter:生成 PDF 文件后重定向
- css - ios14中的奇怪填充
- c - 为什么在使用 fscanf() 之前文件指针的内容为空,而在使用 fscanf() 之后它变成了文件中存在的其余字符串?
- android - 启动自定义软键盘后文本字段不显示光标
- web - 我对大学课程的网络抓取有问题
- html - 无法覆盖 CSS 渐变
- python - 无法从 Django 中的给定位置加载模板
- javascript - 如何在 react js 中为来自 api 的动态表单字段设置一个通用的 onchange?