Powiadomienia lokalne w programie Xamarin.Forms
Powiadomienia lokalne to alerty wysyłane przez aplikacje zainstalowane na urządzeniu przenośnym. Powiadomienia lokalne są często używane w przypadku takich funkcji jak:
- Zdarzenia kalendarza
- Przypomnienia
- Wyzwalacze oparte na lokalizacji
Każda platforma obsługuje tworzenie, wyświetlanie i użycie powiadomień lokalnych w inny sposób. W tym artykule wyjaśniono, jak utworzyć abstrakcję międzyplatformową w celu wysyłania, planowania i odbierania powiadomień lokalnych za pomocą polecenia Xamarin.Forms.
Tworzenie interfejsu międzyplatformowego
Aplikacja Xamarin.Forms powinna tworzyć i korzystać z powiadomień bez obaw dotyczących implementacji podstawowej platformy. INotificationManager
Następujący interfejs jest implementowany w udostępnionej bibliotece kodu i definiuje międzyplatformowy interfejs API, którego aplikacja może używać do interakcji z powiadomieniami:
public interface INotificationManager
{
event EventHandler NotificationReceived;
void Initialize();
void SendNotification(string title, string message, DateTime? notifyTime = null);
void ReceiveNotification(string title, string message);
}
Ten interfejs zostanie zaimplementowany w każdym projekcie platformy. Zdarzenie NotificationReceived
umożliwia aplikacji obsługę powiadomień przychodzących. Metoda Initialize
powinna wykonywać dowolną natywną logikę platformy wymaganą do przygotowania systemu powiadomień. Metoda SendNotification
powinna wysłać powiadomienie w opcjonalnym DateTime
pliku . Metoda ReceiveNotification
powinna być wywoływana przez platformę bazową po odebraniu komunikatu.
Korzystanie z interfejsu w programie Xamarin.Forms
Po utworzeniu interfejsu można go używać w projekcie udostępnionym Xamarin.Forms , mimo że implementacje platformy nie zostały jeszcze utworzone. Przykładowa aplikacja zawiera ContentPage
plik MainPage.xaml z następującą zawartością:
<StackLayout Margin="0,35,0,0"
x:Name="stackLayout">
<Label Text="Click the button below to create a local notification."
TextColor="Red"
HorizontalOptions="Center"
VerticalOptions="Start" />
<Button Text="Create Notification"
HorizontalOptions="Center"
VerticalOptions="Start"
Clicked="OnSendClick" />
<Label Text="Click the button below to schedule a local notification for in 10 seconds time."
TextColor="Red"
HorizontalOptions="Center"
VerticalOptions="Start" />
<Button Text="Create Notification"
HorizontalOptions="Center"
VerticalOptions="Start"
Clicked="OnScheduleClick" />
</StackLayout>
Układ zawiera Label
elementy, które wyjaśniają instrukcje, oraz Button
elementy, które wysyłają lub zaplanują powiadomienie po naciśnięciu.
Klasa MainPage
code-behind obsługuje wysyłanie i odbieranie powiadomień:
public partial class MainPage : ContentPage
{
INotificationManager notificationManager;
int notificationNumber = 0;
public MainPage()
{
InitializeComponent();
notificationManager = DependencyService.Get<INotificationManager>();
notificationManager.NotificationReceived += (sender, eventArgs) =>
{
var evtData = (NotificationEventArgs)eventArgs;
ShowNotification(evtData.Title, evtData.Message);
};
}
void OnSendClick(object sender, EventArgs e)
{
notificationNumber++;
string title = $"Local Notification #{notificationNumber}";
string message = $"You have now received {notificationNumber} notifications!";
notificationManager.SendNotification(title, message);
}
void OnScheduleClick(object sender, EventArgs e)
{
notificationNumber++;
string title = $"Local Notification #{notificationNumber}";
string message = $"You have now received {notificationNumber} notifications!";
notificationManager.SendNotification(title, message, DateTime.Now.AddSeconds(10));
}
void ShowNotification(string title, string message)
{
Device.BeginInvokeOnMainThread(() =>
{
var msg = new Label()
{
Text = $"Notification Received:\nTitle: {title}\nMessage: {message}"
};
stackLayout.Children.Add(msg);
});
}
}
Konstruktor MainPage
klasy używa klasy , Xamarin.FormsDependencyService
aby pobrać wystąpienie specyficzne dla platformy .INotificationManager
Metody OnSendClick
i OnScheduleClicked
używają wystąpienia do wysyłania INotificationManager
i planowania nowych powiadomień. Metoda jest wywoływana ShowNotification
z programu obsługi zdarzeń dołączonego NotificationReceived
do zdarzenia i wstawi nowe Label
na stronie po wywołaniu zdarzenia.
Procedura NotificationReceived
obsługi zdarzeń rzutuje argumenty zdarzeń na NotificationEventArgs
. Ten typ jest zdefiniowany w projekcie udostępnionym Xamarin.Forms :
public class NotificationEventArgs : EventArgs
{
public string Title { get; set; }
public string Message { get; set; }
}
Aby uzyskać więcej informacji o obiekcie Xamarin.FormsDependencyService
, zobacz Xamarin.Forms DependencyService.
Tworzenie implementacji interfejsu systemu Android
Xamarin.Forms Aby aplikacja wysyłała i odbierała powiadomienia w systemie Android, aplikacja musi zapewnić implementację interfejsuINotificationManager
.
Tworzenie klasy AndroidNotificationManager
Klasa AndroidNotificationManager
implementuje INotificationManager
interfejs:
using System;
using Android.App;
using Android.Content;
using Android.Graphics;
using Android.OS;
using AndroidX.Core.App;
using Xamarin.Forms;
using AndroidApp = Android.App.Application;
[assembly: Dependency(typeof(LocalNotifications.Droid.AndroidNotificationManager))]
namespace LocalNotifications.Droid
{
public class AndroidNotificationManager : INotificationManager
{
const string channelId = "default";
const string channelName = "Default";
const string channelDescription = "The default channel for notifications.";
public const string TitleKey = "title";
public const string MessageKey = "message";
bool channelInitialized = false;
int messageId = 0;
int pendingIntentId = 0;
NotificationManager manager;
public event EventHandler NotificationReceived;
public static AndroidNotificationManager Instance { get; private set; }
public AndroidNotificationManager() => Initialize();
public void Initialize()
{
if (Instance == null)
{
CreateNotificationChannel();
Instance = this;
}
}
public void SendNotification(string title, string message, DateTime? notifyTime = null)
{
if (!channelInitialized)
{
CreateNotificationChannel();
}
if (notifyTime != null)
{
Intent intent = new Intent(AndroidApp.Context, typeof(AlarmHandler));
intent.PutExtra(TitleKey, title);
intent.PutExtra(MessageKey, message);
PendingIntent pendingIntent = PendingIntent.GetBroadcast(AndroidApp.Context, pendingIntentId++, intent, PendingIntentFlags.CancelCurrent);
long triggerTime = GetNotifyTime(notifyTime.Value);
AlarmManager alarmManager = AndroidApp.Context.GetSystemService(Context.AlarmService) as AlarmManager;
alarmManager.Set(AlarmType.RtcWakeup, triggerTime, pendingIntent);
}
else
{
Show(title, message);
}
}
public void ReceiveNotification(string title, string message)
{
var args = new NotificationEventArgs()
{
Title = title,
Message = message,
};
NotificationReceived?.Invoke(null, args);
}
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);
NotificationCompat.Builder builder = new NotificationCompat.Builder(AndroidApp.Context, channelId)
.SetContentIntent(pendingIntent)
.SetContentTitle(title)
.SetContentText(message)
.SetLargeIcon(BitmapFactory.DecodeResource(AndroidApp.Context.Resources, Resource.Drawable.xamagonBlue))
.SetSmallIcon(Resource.Drawable.xamagonBlue)
.SetDefaults((int)NotificationDefaults.Sound | (int)NotificationDefaults.Vibrate);
Notification notification = builder.Build();
manager.Notify(messageId++, notification);
}
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;
}
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
}
}
}
Atrybut assembly
powyżej przestrzeni nazw rejestruje implementację interfejsu INotificationManager
za pomocą elementu DependencyService
.
System Android umożliwia aplikacjom definiowanie wielu kanałów dla powiadomień. Metoda Initialize
tworzy podstawowy kanał używany przez przykładową aplikację do wysyłania powiadomień. Metoda SendNotification
definiuje logikę specyficzną dla platformy wymaganą do utworzenia i wysłania powiadomienia. Metoda ReceiveNotification
jest wywoływana przez system operacyjny Android po odebraniu komunikatu i wywołuje procedurę obsługi zdarzeń.
Metoda SendNotification
tworzy powiadomienie lokalne natychmiast lub dokładnie DateTime
. Powiadomienie można zaplanować dla dokładnego DateTime
AlarmManager
użycia klasy, a powiadomienie zostanie odebrane przez obiekt pochodzący z BroadcastReceiver
klasy:
[BroadcastReceiver(Enabled = true, Label = "Local Notifications Broadcast Receiver")]
public class AlarmHandler : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
if (intent?.Extras != null)
{
string title = intent.GetStringExtra(AndroidNotificationManager.TitleKey);
string message = intent.GetStringExtra(AndroidNotificationManager.MessageKey);
AndroidNotificationManager manager = AndroidNotificationManager.Instance ?? new AndroidNotificationManager();
manager.Show(title, message);
}
}
}
Ważne
Domyślnie powiadomienia zaplanowane przy użyciu klasy nie przetrwają ponownego AlarmManager
uruchomienia urządzenia. Można jednak zaprojektować aplikację tak, aby automatycznie zmieniała harmonogram powiadomień w przypadku ponownego uruchomienia urządzenia. Aby uzyskać więcej informacji, zobacz Uruchamianie alarmu po ponownym uruchomieniu urządzenia w harmonogramie powtarzających się alarmów na developer.android.com. Aby uzyskać informacje na temat przetwarzania w tle w systemie Android, zobacz Przewodnik po przetwarzaniu w tle w developer.android.com.
Aby uzyskać więcej informacji na temat odbiorników emisji, zobacz Odbiorniki emisji na platformie Xamarin.Android.
Obsługa powiadomień przychodzących w systemie Android
Klasa MainActivity
musi wykrywać powiadomienia przychodzące i powiadamiać AndroidNotificationManager
wystąpienie. Atrybut Activity
klasy MainActivity
powinien określać LaunchMode
wartość LaunchMode.SingleTop
:
[Activity(
//...
LaunchMode = LaunchMode.SingleTop]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
// ...
}
Tryb SingleTop
uniemożliwia uruchamianie wielu wystąpień elementu Activity
, gdy aplikacja znajduje się na pierwszym planie. Może to LaunchMode
nie być odpowiednie w przypadku aplikacji, które uruchamiają wiele działań w bardziej złożonych scenariuszach powiadomień. Aby uzyskać więcej informacji na temat LaunchMode
wartości wyliczenia, zobacz Android Activity LaunchMode.
W klasie jest modyfikowana w MainActivity
celu odbierania powiadomień przychodzących:
protected override void OnCreate(Bundle 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 title = intent.GetStringExtra(AndroidNotificationManager.TitleKey);
string message = intent.GetStringExtra(AndroidNotificationManager.MessageKey);
DependencyService.Get<INotificationManager>().ReceiveNotification(title, message);
}
}
Metoda CreateNotificationFromIntent
wyodrębnia dane powiadomienia z argumentu intent
i dostarcza je do AndroidNotificationManager
metody przy użyciu ReceiveNotification
metody . Metoda CreateNotificationFromIntent
jest wywoływana zarówno z metody, jak OnCreate
i OnNewIntent
metody:
- Gdy aplikacja zostanie uruchomiona przez dane powiadomień,
Intent
dane zostaną przekazane doOnCreate
metody . - Jeśli aplikacja znajduje się już na pierwszym planie,
Intent
dane zostaną przekazane doOnNewIntent
metody .
System Android oferuje wiele zaawansowanych opcji powiadomień. Aby uzyskać więcej informacji, zobacz Notifications in Xamarin.Android (Powiadomienia na platformie Xamarin.Android).
Tworzenie implementacji interfejsu systemu iOS
Xamarin.Forms Aby aplikacja wysyłała i odbierała powiadomienia w systemie iOS, aplikacja musi zapewnić implementację .INotificationManager
Tworzenie klasy iOSNotificationManager
Klasa iOSNotificationManager
implementuje INotificationManager
interfejs:
using System;
using Foundation;
using UserNotifications;
using Xamarin.Forms;
[assembly: Dependency(typeof(LocalNotifications.iOS.iOSNotificationManager))]
namespace LocalNotifications.iOS
{
public class iOSNotificationManager : INotificationManager
{
int messageId = 0;
bool hasNotificationsPermission;
public event EventHandler NotificationReceived;
public void Initialize()
{
// request the permission to use local notifications
UNUserNotificationCenter.Current.RequestAuthorization(UNAuthorizationOptions.Alert, (approved, err) =>
{
hasNotificationsPermission = approved;
});
}
public void SendNotification(string title, string message, DateTime? notifyTime = null)
{
// EARLY OUT: app doesn't have permissions
if (!hasNotificationsPermission)
{
return;
}
messageId++;
var content = new UNMutableNotificationContent()
{
Title = title,
Subtitle = "",
Body = message,
Badge = 1
};
UNNotificationTrigger trigger;
if (notifyTime != null)
{
// Create a calendar-based trigger.
trigger = UNCalendarNotificationTrigger.CreateTrigger(GetNSDateComponents(notifyTime.Value), false);
}
else
{
// Create a time-based trigger, interval is in seconds and must be greater than 0.
trigger = UNTimeIntervalNotificationTrigger.CreateTrigger(0.25, false);
}
var request = UNNotificationRequest.FromIdentifier(messageId.ToString(), content, trigger);
UNUserNotificationCenter.Current.AddNotificationRequest(request, (err) =>
{
if (err != null)
{
throw new Exception($"Failed to schedule notification: {err}");
}
});
}
public void ReceiveNotification(string title, string message)
{
var args = new NotificationEventArgs()
{
Title = title,
Message = message
};
NotificationReceived?.Invoke(null, args);
}
NSDateComponents GetNSDateComponents(DateTime dateTime)
{
return new NSDateComponents
{
Month = dateTime.Month,
Day = dateTime.Day,
Year = dateTime.Year,
Hour = dateTime.Hour,
Minute = dateTime.Minute,
Second = dateTime.Second
};
}
}
}
Atrybut assembly
powyżej przestrzeni nazw rejestruje implementację interfejsu INotificationManager
za pomocą elementu DependencyService
.
W systemie iOS należy zażądać uprawnień do korzystania z powiadomień przed podjęciem próby zaplanowana powiadomienia. Metoda Initialize
żąda autoryzacji do korzystania z powiadomień lokalnych. Metoda SendNotification
definiuje logikę wymaganą do utworzenia i wysłania powiadomienia. Metoda ReceiveNotification
zostanie wywołana przez system iOS po odebraniu komunikatu i wywołuje procedurę obsługi zdarzeń.
Uwaga
Metoda SendNotification
tworzy powiadomienie lokalne natychmiast, przy użyciu UNTimeIntervalNotificationTrigger
obiektu lub dokładnie DateTime
przy użyciu UNCalendarNotificationTrigger
obiektu.
Obsługa powiadomień przychodzących w systemie iOS
W systemie iOS należy utworzyć delegata, który podklasy UNUserNotificationCenterDelegate
obsługuje komunikaty przychodzące. Przykładowa aplikacja definiuje klasę iOSNotificationReceiver
:
public class iOSNotificationReceiver : UNUserNotificationCenterDelegate
{
public override void WillPresentNotification(UNUserNotificationCenter center, UNNotification notification, Action<UNNotificationPresentationOptions> completionHandler)
{
ProcessNotification(notification);
completionHandler(UNNotificationPresentationOptions.Alert);
}
void ProcessNotification(UNNotification notification)
{
string title = notification.Request.Content.Title;
string message = notification.Request.Content.Body;
DependencyService.Get<INotificationManager>().ReceiveNotification(title, message);
}
}
Ta klasa używa DependencyService
klasy , aby uzyskać wystąpienie iOSNotificationManager
klasy i dostarcza przychodzące dane powiadomień do ReceiveNotification
metody .
Klasa AppDelegate
musi określić iOSNotificationReceiver
obiekt jako delegat podczas uruchamiania UNUserNotificationCenter
aplikacji. Dzieje się tak w metodzie FinishedLaunching
:
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init();
UNUserNotificationCenter.Current.Delegate = new iOSNotificationReceiver();
LoadApplication(new App());
return base.FinishedLaunching(app, options);
}
System iOS oferuje wiele zaawansowanych opcji powiadomień. Aby uzyskać więcej informacji, zobacz Powiadomienia w środowisku Xamarin.iOS.
Testowanie aplikacji
Gdy projekty platformy zawierają zarejestrowaną implementację interfejsu INotificationManager
, aplikację można przetestować na obu platformach. Uruchom aplikację i kliknij jeden z przycisków Utwórz powiadomienie , aby utworzyć powiadomienie.
W systemie Android powiadomienia będą wyświetlane w obszarze powiadomień. Po naciśnięciu powiadomienia aplikacja odbiera powiadomienie i wyświetla komunikat:
W systemie iOS powiadomienia przychodzące są automatycznie odbierane przez aplikację bez konieczności wprowadzania danych przez użytkownika. Aplikacja odbiera powiadomienie i wyświetla komunikat: