中的本機通知 Xamarin.Forms
本機通知是由安裝在行動裝置上的應用程式所傳送的警示。 本機通知通常用於下列功能:
- 行事曆事件
- 提醒
- 以位置為基礎的觸發程式
每個平台都會以不同的方式處理本機通知的建立、顯示和取用。 本文說明如何建立跨平臺抽象概念,以使用 Xamarin.Forms傳送、排程及接收本機通知。
建立跨平臺介面
應用程式 Xamarin.Forms 應該建立和使用通知,而不需考慮基礎平台實作。 下列 INotificationManager
介面會在共用程式代碼連結庫中實作,並定義應用程式可用來與通知互動的跨平臺 API:
public interface INotificationManager
{
event EventHandler NotificationReceived;
void Initialize();
void SendNotification(string title, string message, DateTime? notifyTime = null);
void ReceiveNotification(string title, string message);
}
此介面將會在每個平台項目中實作。 事件 NotificationReceived
可讓應用程式處理傳入通知。 Initialize
方法應該執行準備通知系統所需的任何原生平台邏輯。 方法 SendNotification
應該會在選擇性 DateTime
的傳送通知。 收到 ReceiveNotification
訊息時,基礎平臺應該呼叫 方法。
在中使用介面 Xamarin.Forms
建立介面之後,即使尚未建立平台實作,仍可在共用 Xamarin.Forms 專案中取用它。 範例應用程式包含 ContentPage
名為 MainPage.xaml 且內容如下:
<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>
版面配置包含 Label
說明指示的專案,以及 Button
點選時傳送或排程通知的專案。
類別程式 MainPage
代碼後置會處理通知的傳送和接收:
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);
});
}
}
類別建 MainPage
構函式會使用 Xamarin.FormsDependencyService
來擷取 平臺特定的實例 INotificationManager
。 OnSendClick
和 OnScheduleClicked
方法會INotificationManager
使用 實例來傳送和排程新的通知。 從 ShowNotification
附加至 NotificationReceived
事件的事件處理程式呼叫 方法,並在叫用事件時將新的 Label
插入頁面。
NotificationReceived
事件處理程式會將其事件自變數NotificationEventArgs
轉換成 。 這個類型定義於共享 Xamarin.Forms 專案中:
public class NotificationEventArgs : EventArgs
{
public string Title { get; set; }
public string Message { get; set; }
}
如需 的詳細資訊 Xamarin.FormsDependencyService
,請參閱 Xamarin.Forms DependencyService。
建立Android介面實作
若要讓 Xamarin.Forms 應用程式在Android上傳送和接收通知,應用程式必須提供介面的實作 INotificationManager
。
建立 AndroidNotificationManager 類別
類別 AndroidNotificationManager
會實作 INotificationManager
介面:
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
}
}
}
命名空間 assembly
上方的屬性會 INotificationManager
向 DependencyService
註冊介面實作。
Android 可讓應用程式定義通知的多個通道。 方法 Initialize
會建立範例應用程式用來傳送通知的基本通道。 方法 SendNotification
會定義建立和傳送通知所需的平臺特定邏輯。 ReceiveNotification
收到訊息時,Android OS 會呼叫 方法,並叫用事件處理程式。
方法 SendNotification
會立即建立本機通知,或直接 DateTime
建立 。 您可以使用 類別來排程通知DateTime
AlarmManager
,而衍生自 BroadcastReceiver
類別的物件將會收到通知:
[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);
}
}
}
重要
根據預設,使用 類別排程的 AlarmManager
通知將不會在裝置重新啟動時存留。 不過,您可以設計應用程式,以在裝置重新啟動時自動重新排程通知。 如需詳細資訊,請參閱在排程重複警示 developer.android.com 中重新啟動裝置時啟動警示。 如需Android上背景處理的相關信息,請參閱 developer.android.com 背景處理 指南。
如需廣播接收器的詳細資訊,請參閱 Xamarin.Android 中的廣播接收器。
處理Android上的傳入通知
類別 MainActivity
必須偵測傳入通知,並通知 AndroidNotificationManager
實例。 類別 Activity
上的 MainActivity
屬性應該指定 LaunchMode
的值 LaunchMode.SingleTop
:
[Activity(
//...
LaunchMode = LaunchMode.SingleTop]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
// ...
}
模式 SingleTop
可防止在應用程式處於前景時啟動的多個實例 Activity
。 這可能 LaunchMode
不適用於在更複雜的通知案例中啟動多個活動的應用程式。 如需列舉值的詳細資訊 LaunchMode
,請參閱 Android 活動 LaunchMode。
在類別中 MainActivity
,會修改為接收傳入通知:
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);
}
}
方法 CreateNotificationFromIntent
會從 自變數擷 intent
取通知資料,並使用 方法提供給 AndroidNotificationManager
ReceiveNotification
。 從 CreateNotificationFromIntent
方法和 OnNewIntent
方法呼叫 OnCreate
方法:
- 當應用程式由通知數據啟動時,
Intent
數據會傳遞至OnCreate
方法。 - 如果應用程式已經在前景中,
Intent
數據將會傳遞至OnNewIntent
方法。
Android 提供許多通知的進階選項。 如需詳細資訊,請參閱 Xamarin.Android 中的通知。
建立 iOS 介面實作
若要讓 Xamarin.Forms 應用程式在 iOS 上傳送和接收通知,應用程式必須提供的 INotificationManager
實作。
建立 iOSNotificationManager 類別
類別 iOSNotificationManager
會實作 INotificationManager
介面:
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
};
}
}
}
命名空間 assembly
上方的屬性會 INotificationManager
向 DependencyService
註冊介面實作。
在 iOS 上,您必須要求許可權才能使用通知,才能嘗試排程通知。 方法 Initialize
會要求授權以使用本機通知。 方法 SendNotification
會定義建立和傳送通知所需的邏輯。 ReceiveNotification
收到訊息時,iOS 會呼叫 方法,並叫用事件處理程式。
注意
方法會 SendNotification
立即使用 UNTimeIntervalNotificationTrigger
物件建立本機通知,或直接 DateTime
使用 物件建立本機 UNCalendarNotificationTrigger
通知。
處理 iOS 上的傳入通知
在 iOS 上,您必須建立子類別 UNUserNotificationCenterDelegate
的委派來處理傳入訊息。 範例應用程式會定義類別 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);
}
}
這個類別會使用 DependencyService
取得 類別的 iOSNotificationManager
實例,並將傳入通知數據 ReceiveNotification
提供給方法。
類別 AppDelegate
必須在應用程式啟動期間指定 iOSNotificationReceiver
物件做為 UNUserNotificationCenter
委派。 這發生在 方法中 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);
}
iOS 提供許多通知的進階選項。 如需詳細資訊,請參閱 Xamarin.iOS 中的通知。
測試應用程式
一旦平台專案包含介面的已註冊實作 INotificationManager
,就可以在兩個平臺上測試應用程式。 執行應用程式,然後按兩下其中一個 [建立通知 ] 按鈕來建立通知。
在 Android 上,通知會出現在通知區域中。 點選通知時,應用程式會收到通知並顯示訊息:
在 iOS 上,應用程式會自動收到傳入通知,而不需要使用者輸入。 應用程式會收到通知,並顯示訊息: