首页 > 解决方案 > Intent extra 总是返回 null - xamarin 表单 - ScheduledAlarmHandler - 本地通知

问题描述

我正在尝试在我的 xamarin 表单应用程序中实现重复提醒系统。我已从此链接https://www.c-sharpcorner.com/article/how-to-send-local-notification-with-repeat-interval-in-xamarin-forms/获取代码并删除了已弃用的代码,用新代码(通知通道等)修改它

我能够创建一个意图并将序列化的通知字符串放入其中,并在 15 秒后启动警报活动以触发。断点在 15 秒后完美触发,但由于接收到的意图在额外字段中有一个空值,我无法反序列化字符串,因此应用程序在调试模式和非调试模式下退出,没有收到通知.

这是我的代码:

这是我用来引用键的静态类...

public static class NotificationEssentials
{
    public static string channelId = "default";
    public static string channelName = "Default";
    public static string channelDescription = "The default channel for notifications.";
    public static int alarmRequestCode = 8748423;
}

这是 Android proj 中的 NotificationService 类

public class LocalNotificationService : ILocalNotificationService
{
    int _notificationIconId { get; set; }
    readonly DateTime _jan1st1970 = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
    NotificationManager manager;

    public void LocalNotification(string title, string body, int id, DateTime notifyTime)
    {
        long repeateForMinute = 60000;
        long totalMilliSeconds = (long)(notifyTime.ToUniversalTime() - _jan1st1970).TotalMilliseconds;
        if (totalMilliSeconds < JavaSystem.CurrentTimeMillis())
            totalMilliSeconds = totalMilliSeconds + repeateForMinute;

        Intent intent = CreateIntent(id);
        LocalNotification localNotification = new LocalNotification
        {
            Title = title,
            Body = body,
            Id = id,
            NotifyTime = notifyTime
        };
        if (_notificationIconId != 0)
            localNotification.IconId = _notificationIconId;
        else
            localNotification.IconId = Resource.Drawable.book;

        var serializedNotification = SerializeNotification(localNotification);
        intent.PutExtra(ScheduledAlarmHandler.LocalNotificationKey, serializedNotification);

        var pendingIntent = PendingIntent.GetBroadcast(Application.Context, NotificationEssentials.alarmRequestCode, 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, NotificationEssentials.alarmRequestCode, intent, PendingIntentFlags.Immutable);
        var alarmManager = GetAlarmManager();
        alarmManager.Cancel(pendingIntent);
        NotificationManagerCompat notificationManager = NotificationManagerCompat.From(Application.Context);
        notificationManager.CancelAll();
        notificationManager.Cancel(id);
    }

    public static Intent GetLauncherActivity()
    {
        return Application.Context.PackageManager.GetLaunchIntentForPackage(Application.Context.PackageName);
    }

    private Intent CreateIntent(int id)
    {
        return new Intent(Application.Context, typeof(ScheduledAlarmHandler));//.SetAction("LocalNotifierIntent" + id);
    }

    private AlarmManager GetAlarmManager()
    {
        return Application.Context.GetSystemService(Context.AlarmService) as AlarmManager;
    }

    private string SerializeNotification(LocalNotification notification)
    {
        XmlSerializer xmlSerializer = new XmlSerializer(notification.GetType());
        using (var stringWriter = new StringWriter())
        {
            xmlSerializer.Serialize(stringWriter, notification);
            return stringWriter.ToString();
        }
    }

    void CreateNotificationChannel()
    {
        manager = (NotificationManager)Application.Context.GetSystemService(Context.NotificationService);
        if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
        {
            var channelNameJava = new Java.Lang.String(NotificationEssentials.channelName);
            var channel = new NotificationChannel(NotificationEssentials.channelId, channelNameJava, NotificationImportance.Default)
            {
                Description = NotificationEssentials.channelDescription
            };
            manager.CreateNotificationChannel(channel);
        }
        //channelInitialized = true;
    }
}

这是广播接收器类

[BroadcastReceiver(Enabled = true, Label = "Local Notifications Broadcast Receiver")]
public class ScheduledAlarmHandler : BroadcastReceiver
{
    public const string LocalNotificationKey = "LocalNotification";
    public override void OnReceive(Context context, Intent intent)
    {
        var extra = intent.GetStringExtra(LocalNotificationKey);
        var notification = DeserializeNotification(extra);

        //Generating notification    

        NotificationCompat.Builder builder = new NotificationCompat.Builder(Application.Context, NotificationEssentials.channelId)
            .SetContentTitle(notification.Title)
            .SetContentText(notification.Body)
            .SetSmallIcon(notification.IconId)
            .SetDefaults((int)NotificationDefaults.Sound | (int)NotificationDefaults.Vibrate);

        var resultIntent = LocalNotificationService.GetLauncherActivity();
        resultIntent.SetFlags(ActivityFlags.NewTask | ActivityFlags.ClearTask);
        var stackBuilder = Android.Support.V4.App.TaskStackBuilder.Create(Application.Context);
        stackBuilder.AddNextIntent(resultIntent);

        //Random random = new Random();
        //int randomNumber = random.Next(9999 - 1000) + 1000;

        var resultPendingIntent =
            stackBuilder.GetPendingIntent(NotificationEssentials.alarmRequestCode, (int)PendingIntentFlags.Immutable);
        builder.SetContentIntent(resultPendingIntent);

        // Sending notification    
        var notificationManager = NotificationManagerCompat.From(Application.Context);
        notificationManager.Notify(-1, 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;
        }
    }
}

标签: c#androidxamarin.forms

解决方案


我正在使用受尊敬的客户端项目(android和ios)中的警报服务发布重复本地通知的工作代码

这是 PCL 项目的接口

public interface ILocalNotificationService
{
    void LocalNotification(string title, string body, int id, DateTime notifyTime);
    void Cancel(int id);
}

这是本地通知模型类

public class LocalNotification
{
    public string Title { get; set; }

    public string Body { get; set; }

    public int Id { get; set; }

    public int IconId { get; set; }

    public DateTime NotifyTime { get; set; }
}

这是android proj中使用的服务类

using System;
using System.IO;
using System.Xml.Serialization;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Support.V4.App;
using YOURAPPLICATION.AppServices;
using YOURAPPLICATION.Droid;
using YOURAPPLICATION.Interfaces;
using Java.Lang;

[assembly: Xamarin.Forms.Dependency(typeof(LocalNotificationService))]
namespace YOURAPPLICATION.Droid
{

    public static class NotificationEssentials
    {
        public static string channelId = "default";
        public static string channelName = "Default";
        public static string channelDescription = "The default channel for notifications.";
        public static int alarmRequestCode = 8748423;
    }

    public class LocalNotificationService : ILocalNotificationService
    {
        int _notificationIconId { get; set; }
        readonly DateTime _jan1st1970 = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);

        public void LocalNotification(string title, string body, int id, DateTime notifyTime)
        {
            long repeatEachDay = 1000 * 60 * 60 * 24;
            //long repeateEachMinute = 60000;
            long totalMilliSeconds = (long)(notifyTime.ToUniversalTime() - _jan1st1970).TotalMilliseconds;
            if (totalMilliSeconds < JavaSystem.CurrentTimeMillis())
                totalMilliSeconds = totalMilliSeconds + repeatEachDay;

            Intent intent = CreateIntent(id);
            LocalNotification localNotification = new LocalNotification
            {
                Title = title,
                Body = body,
                Id = id,
                NotifyTime = notifyTime
            };
            if (_notificationIconId != 0)
                localNotification.IconId = _notificationIconId;
            else
                localNotification.IconId = Resource.Drawable.book;

            var serializedNotification = SerializeNotification(localNotification);
            intent.PutExtra(ScheduledAlarmHandler.LocalNotificationKey, serializedNotification);

            Random generator = new Random();
            string 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, repeatEachDay, pendingIntent);
        }

        public void Cancel(int id)
        {
            var intent = CreateIntent(id);
            var pendingIntent = PendingIntent.GetBroadcast(Application.Context, NotificationEssentials.alarmRequestCode, intent, PendingIntentFlags.Immutable);
            var alarmManager = GetAlarmManager();
            alarmManager.Cancel(pendingIntent);
            NotificationManagerCompat notificationManager = NotificationManagerCompat.From(Application.Context);
            notificationManager.CancelAll();
            notificationManager.Cancel(id);
        }

        public static Intent GetLauncherActivity()
        {
            return Application.Context.PackageManager.GetLaunchIntentForPackage(Application.Context.PackageName);
        }

        private Intent CreateIntent(int id)
        {
            return new Intent(Application.Context, typeof(ScheduledAlarmHandler)).SetAction("LocalNotifierIntent" + id);
        }

        private AlarmManager GetAlarmManager()
        {
            return Application.Context.GetSystemService(Context.AlarmService) as AlarmManager;
        }

        private string SerializeNotification(LocalNotification notification)
        {
            XmlSerializer 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";
        bool channelInitialized = false;
        NotificationManager manager;

        public override void OnReceive(Context context, Intent intent)
        {
            var extra = intent.GetStringExtra(LocalNotificationKey);
            var notification = DeserializeNotification(extra);

            if (!channelInitialized)
            {
                CreateNotificationChannel();
            }

            NotificationCompat.Builder builder = new NotificationCompat.Builder(Application.Context, NotificationEssentials.channelId)
                .SetContentTitle(notification.Title)
                .SetContentText(notification.Body)
                .SetSmallIcon(notification.IconId)
                .SetAutoCancel(true)
                .SetStyle(new NotificationCompat.BigTextStyle()
                    .BigText("Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."))
                .SetDefaults((int)NotificationDefaults.Sound | (int)NotificationDefaults.Vibrate);

            var resultIntent = LocalNotificationService.GetLauncherActivity();
            resultIntent.SetFlags(ActivityFlags.NewTask | ActivityFlags.ClearTask);
            var stackBuilder = Android.Support.V4.App.TaskStackBuilder.Create(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);

            manager.Notify(randomNumber, builder.Build());

        }

        void CreateNotificationChannel()
        {
            manager = (NotificationManager)Application.Context.GetSystemService(Context.NotificationService);
            if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
            {
                var channelNameJava = new Java.Lang.String(NotificationEssentials.channelName);
                var channel = new NotificationChannel(NotificationEssentials.channelId, channelNameJava, NotificationImportance.Default)
                {
                    Description = NotificationEssentials.channelDescription
                };
                manager.CreateNotificationChannel(channel);
            }
            //channelInitialized = true;
        }

        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;
            }
        }
    }
}

最后是IOS服务类

using System;
using System.Linq;
using Foundation;
using UIKit;
using Xamarin.Forms;
using IslamicApp.iOS;
using IslamicApp.Interfaces;

[assembly: Dependency(typeof(LocalNotificationService))]
namespace IslamicApp.iOS
{
    public class LocalNotificationService : ILocalNotificationService
    {

        const string NotificationKey = "LocalNotificationKey";

        public void LocalNotification(string title, string body, int id, DateTime notifyTime)
        {
            var notification = new UILocalNotification
            {
                AlertTitle = title,
                AlertBody = body,
                SoundName = UILocalNotification.DefaultSoundName,
                FireDate = notifyTime.ToNSDate(),
                RepeatInterval = NSCalendarUnit.Minute,
                UserInfo = NSDictionary.FromObjectAndKey(NSObject.FromObject(id), NSObject.FromObject(NotificationKey))
            };
            UIApplication.SharedApplication.ScheduleLocalNotification(notification);
        }

        public void Cancel(int id)
        {
            var notifications = UIApplication.SharedApplication.ScheduledLocalNotifications;
            var notification = notifications.Where(n => n.UserInfo.ContainsKey(NSObject.FromObject(NotificationKey)))
                .FirstOrDefault(n => n.UserInfo[NotificationKey].Equals(NSObject.FromObject(id)));
            UIApplication.SharedApplication.CancelAllLocalNotifications();
            if (notification != null)
            {
                UIApplication.SharedApplication.CancelLocalNotification(notification);
                UIApplication.SharedApplication.CancelAllLocalNotifications();
            }
        }
    }

    public static class DateTimeExtensions
    {
        static DateTime nsUtcRef = new DateTime(2001, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
        // last zero is milliseconds    

        public static double SecondsSinceNSRefenceDate(this DateTime dt)
        {
            return (dt.ToUniversalTime() - nsUtcRef).TotalSeconds;
        }

        public static NSDate ToNSDate(this DateTime dt)
        {
            return NSDate.FromTimeIntervalSinceReferenceDate(dt.SecondsSinceNSRefenceDate());
        }
    }
}

而已

注意:我没有检查此代码是否在 IOS 中按预期工作。

更新:这是 PCL 的用法

var SelectedDate = DateTime.Now;
var SelectedTime = DateTime.Now.AddMinutes(1);
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);

DependencyService.Get<ILocalNotificationService>().Cancel(0);
DependencyService.Get<ILocalNotificationService>().LocalNotification("Sample Notification",
                    "Hi, This is a sample notification from Xamarin forms", 0, selectedDateTime);

推荐阅读