首页 > 解决方案 > 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);
        }
    }

标签: c#androidxamarin

解决方案


我在后台进程中加载​​ SQL 数据时遇到的问题。依赖服务导致了这个问题。我通过使用 Android Device Monitor 工具并借助以下工具解决了这个问题:Xamarin Service and Forms Init


推荐阅读