我可以让 Xamarin 应用程序进行后台刷新并向用户发送通知吗?
Can I make a Xamarin app do a background refresh and send a notification to the user?
我正在学习 Xamarin,我想实现一种方法,让应用程序大约每 20 分钟左右在后台发出一次 HttpClient 请求,并根据从请求中获得的数据向用户发送推送通知回复。我相当有信心这是可能的,但通过在互联网上的搜索,我还没有在 Xamarin.Forms.
中看到执行此操作的方法
这需要完成 platform-specific。
使用 android 调度请求会更简单一些,因为这可以通过后台服务 运行 周期性任务和广播接收器来实现。您可以在此处阅读更多相关信息 https://docs.microsoft.com/en-us/xamarin/android/app-fundamentals/services/ and https://docs.microsoft.com/en-us/xamarin/android/app-fundamentals/broadcast-receivers.
在共享项目中:
public class BackgroundJobManager
{
private int _currentId;
private readonly List<BackgroundJob> _backgroundJobs = new List<BackgroundJob>();
public IReadOnlyList<BackgroundJob> BackgroundJobs => _backgroundJobs;
private int NextId() => Interlocked.Increment(ref _currentId);
public int AddJob(Action action, TimeSpan delay, TimeSpan interval, string description)
{
var nextJobId = NextId();
MessagingCenter.Subscribe<IAlarmReceiver>(this, nextJobId.ToString(), (sender) => action());
_backgroundJobs.Add(new BackgroundJob() { DueTime = delay, Interval = interval, ID = nextJobId });
return nextJobId;
}
public void RemoveJob(int jobId)
{
MessagingCenter.Unsubscribe<IAlarmReceiver>(this, jobId.ToString());
_backgroundJobs.RemoveWhere(job => job.ID == jobId);
}
}
INotificationManager
public interface INotificationManager
{
event EventHandler<NotificationEventArgs> NotificationReceived;
void Initialize();
void SendNotification(string title, string message);
void ReceiveNotification(string title, string message);
}
NotificationEventArgs
public class NotificationEventArgs : System.EventArgs
{
public string Title { get; set; }
public string Message { get; set; }
}
IAlarmReceiver
public interface IAlarmReceiver
{
}
在您提出请求的 class 中:
private void SetUpBackgroundRequest()
{
Action startBackGroundJob = async () => await MakeYourRequestHere();
_backgroundJobManager.AddJob(startBackGroundJob, TimeSpan.FromSeconds(10), TimeSpan.FromMinutes(20), "Making request");
}
Android
在您的 MainActivity.cs 中添加以下内容:
private Intent _serviceIntent;
protected override void OnPause()
{
base.OnPause();
_serviceIntent = new Intent(this, typeof(PeriodicService));
StartService(_serviceIntent);
}
protected override void OnResume()
{
base.OnResume();
if (_serviceIntent != null)
StopService(_serviceIntent);
}
定期服务
[Service]
class PeriodicService : Service
{
private static AlarmHandler alarm = new AlarmHandler();
private readonly BackgroundJobManager _backgroundJobManager = new YourSharedProject.BackgroundJobManager();
public override StartCommandResult OnStartCommand(Intent intent,
StartCommandFlags flags, int startId)
{
foreach(var job in _backgroundJobManager.BackgroundJobs)
{
alarm.SetAlarm(job.DueTime, job.Interval, job.ID);
}
return StartCommandResult.Sticky;
}
public override bool StopService(Intent name)
{
foreach (var job in _backgroundJobManager.BackgroundJobs)
{
alarm.UnsetAlarm(job.ID);
}
return base.StopService(name);
}
public override void OnDestroy()
{
base.OnDestroy();
}
public override IBinder OnBind(Intent intent)
{
return null;
}
}
AndroidNotificationManager
public class AndroidNotificationManager : YourSharedProject.NotificationManager
{
private const string ChannelId = "default";
private const string ChannelName = "Default";
private const string ChannelDescription = "The default channel for notifications.";
public const string TitleKey = "title";
public const string MessageKey = "message";;
private bool channelInitialized = false;
private int messageId = 0;
private int pendingIntentId = 0;
private NotificationManager manager;
public static AndroidNotificationManager Instance { get; private set; }
public AndroidNotificationManager() => Initialize();
public override void Initialize()
{
if (Instance == null)
{
CreateNotificationChannel();
Instance = this;
}
}
public override void SendNotification(string title, string message)
{
if (!channelInitialized)
{
CreateNotificationChannel();
}
Show(title, message);
}
public void Show(string title, string message)
{
Interlocked.Increment(ref messageId);
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.BigTextStyle textStyle = new NotificationCompat.BigTextStyle();
NotificationCompat.Builder builder = new NotificationCompat.Builder(AndroidApp.Context, ChannelId)
.SetContentIntent(pendingIntent)
.SetContentTitle(title)
.SetContentText(message)
.SetStyle(textStyle)
.SetLargeIcon(BitmapFactory.DecodeResource(AndroidApp.Context.Resources, Resource.Drawable.launchIcon))
.SetSmallIcon(Resource.Drawable.launchIcon)
.SetDefaults((int)NotificationDefaults.All)
.SetAutoCancel(true);
Notification notification = builder.Build();
manager.Notify(messageId, notification);
}
private void CreateNotificationChannel()
{
manager = (NotificationManager)AndroidApp.Context.GetSystemService(Context.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;
}
}
报警接收器
[BroadcastReceiver]
class AlarmReciver : BroadcastReceiver, IAlarmReceiver
{
public override void OnReceive(Context context, Intent intent)
{
var id = intent.GetStringExtra("jobId");
MessagingCenter.Send<IAlarmReceiver>(this, id);
}
}
[BroadcastReceiver(Enabled = true, Label = "Local Notifications Broadcast Receiver")]
class AlarmHandler : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
if (intent?.Extras != null)
{
string title = intent.GetStringExtra(View.AndroidNotificationManager.TitleKey);
string message = intent.GetStringExtra(View.AndroidNotificationManager.MessageKey);
View.AndroidNotificationManager manager = View.AndroidNotificationManager.Instance ?? new View.AndroidNotificationManager();
manager.Show(title, message, assignmentId);
}
}
public void SetAlarm(TimeSpan dueTime, TimeSpan interval, int jobId)
{
var alarmIntent = new Intent(Application.Context, typeof(AlarmReciver));
alarmIntent.PutExtra("jobId", jobId.ToString());
var pending = PendingIntent.GetBroadcast(Application.Context,
jobId, alarmIntent, PendingIntentFlags.UpdateCurrent);
var alarmManager = Application.Context.GetSystemService(Context.AlarmService)
.JavaCast<AlarmManager>();
alarmManager.SetRepeating(AlarmType.RtcWakeup, (long)dueTime.TotalMilliseconds,
(long)interval.TotalMilliseconds, pending);
}
public void UnsetAlarm(int jobId)
{
var alarmIntent = new Intent(Application.Context, typeof(AlarmReciver));
var pending = PendingIntent.GetBroadcast(Application.Context,
jobId, alarmIntent, PendingIntentFlags.UpdateCurrent);
var alarmManager = Application.Context.GetSystemService(Context.AlarmService)
.JavaCast<AlarmManager>();
alarmManager.Cancel(pending);
}
}
对于 iOS 它有点复杂。 IOS 提供用于刷新 non-critcal 内容的后台提取。无法选择间隔,因为 UIApplication.BackgroundFetchIntervalMinimum 取决于很多因素,例如应用程序使用情况、电池寿命等。您仍然希望使用 UIApplication.BackgroundFetchIntervalMinimum 来获取内容 尽可能多。
另一种方法是使用远程通知,使用 Apple 推送通知服务 (APN)。
此处的信息和示例 https://docs.microsoft.com/en-us/xamarin/ios/app-fundamentals/backgrounding/ios-backgrounding-techniques/updating-an-application-in-the-background.
两个平台的另一个选择是 Azure,如此处所述https://docs.microsoft.com/en-us/azure/developer/mobile-apps/notification-hubs-backend-service-xamarin-forms
我正在学习 Xamarin,我想实现一种方法,让应用程序大约每 20 分钟左右在后台发出一次 HttpClient 请求,并根据从请求中获得的数据向用户发送推送通知回复。我相当有信心这是可能的,但通过在互联网上的搜索,我还没有在 Xamarin.Forms.
中看到执行此操作的方法这需要完成 platform-specific。 使用 android 调度请求会更简单一些,因为这可以通过后台服务 运行 周期性任务和广播接收器来实现。您可以在此处阅读更多相关信息 https://docs.microsoft.com/en-us/xamarin/android/app-fundamentals/services/ and https://docs.microsoft.com/en-us/xamarin/android/app-fundamentals/broadcast-receivers.
在共享项目中:
public class BackgroundJobManager
{
private int _currentId;
private readonly List<BackgroundJob> _backgroundJobs = new List<BackgroundJob>();
public IReadOnlyList<BackgroundJob> BackgroundJobs => _backgroundJobs;
private int NextId() => Interlocked.Increment(ref _currentId);
public int AddJob(Action action, TimeSpan delay, TimeSpan interval, string description)
{
var nextJobId = NextId();
MessagingCenter.Subscribe<IAlarmReceiver>(this, nextJobId.ToString(), (sender) => action());
_backgroundJobs.Add(new BackgroundJob() { DueTime = delay, Interval = interval, ID = nextJobId });
return nextJobId;
}
public void RemoveJob(int jobId)
{
MessagingCenter.Unsubscribe<IAlarmReceiver>(this, jobId.ToString());
_backgroundJobs.RemoveWhere(job => job.ID == jobId);
}
}
INotificationManager
public interface INotificationManager
{
event EventHandler<NotificationEventArgs> NotificationReceived;
void Initialize();
void SendNotification(string title, string message);
void ReceiveNotification(string title, string message);
}
NotificationEventArgs
public class NotificationEventArgs : System.EventArgs
{
public string Title { get; set; }
public string Message { get; set; }
}
IAlarmReceiver
public interface IAlarmReceiver
{
}
在您提出请求的 class 中:
private void SetUpBackgroundRequest()
{
Action startBackGroundJob = async () => await MakeYourRequestHere();
_backgroundJobManager.AddJob(startBackGroundJob, TimeSpan.FromSeconds(10), TimeSpan.FromMinutes(20), "Making request");
}
Android 在您的 MainActivity.cs 中添加以下内容:
private Intent _serviceIntent;
protected override void OnPause()
{
base.OnPause();
_serviceIntent = new Intent(this, typeof(PeriodicService));
StartService(_serviceIntent);
}
protected override void OnResume()
{
base.OnResume();
if (_serviceIntent != null)
StopService(_serviceIntent);
}
定期服务
[Service]
class PeriodicService : Service
{
private static AlarmHandler alarm = new AlarmHandler();
private readonly BackgroundJobManager _backgroundJobManager = new YourSharedProject.BackgroundJobManager();
public override StartCommandResult OnStartCommand(Intent intent,
StartCommandFlags flags, int startId)
{
foreach(var job in _backgroundJobManager.BackgroundJobs)
{
alarm.SetAlarm(job.DueTime, job.Interval, job.ID);
}
return StartCommandResult.Sticky;
}
public override bool StopService(Intent name)
{
foreach (var job in _backgroundJobManager.BackgroundJobs)
{
alarm.UnsetAlarm(job.ID);
}
return base.StopService(name);
}
public override void OnDestroy()
{
base.OnDestroy();
}
public override IBinder OnBind(Intent intent)
{
return null;
}
}
AndroidNotificationManager
public class AndroidNotificationManager : YourSharedProject.NotificationManager
{
private const string ChannelId = "default";
private const string ChannelName = "Default";
private const string ChannelDescription = "The default channel for notifications.";
public const string TitleKey = "title";
public const string MessageKey = "message";;
private bool channelInitialized = false;
private int messageId = 0;
private int pendingIntentId = 0;
private NotificationManager manager;
public static AndroidNotificationManager Instance { get; private set; }
public AndroidNotificationManager() => Initialize();
public override void Initialize()
{
if (Instance == null)
{
CreateNotificationChannel();
Instance = this;
}
}
public override void SendNotification(string title, string message)
{
if (!channelInitialized)
{
CreateNotificationChannel();
}
Show(title, message);
}
public void Show(string title, string message)
{
Interlocked.Increment(ref messageId);
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.BigTextStyle textStyle = new NotificationCompat.BigTextStyle();
NotificationCompat.Builder builder = new NotificationCompat.Builder(AndroidApp.Context, ChannelId)
.SetContentIntent(pendingIntent)
.SetContentTitle(title)
.SetContentText(message)
.SetStyle(textStyle)
.SetLargeIcon(BitmapFactory.DecodeResource(AndroidApp.Context.Resources, Resource.Drawable.launchIcon))
.SetSmallIcon(Resource.Drawable.launchIcon)
.SetDefaults((int)NotificationDefaults.All)
.SetAutoCancel(true);
Notification notification = builder.Build();
manager.Notify(messageId, notification);
}
private void CreateNotificationChannel()
{
manager = (NotificationManager)AndroidApp.Context.GetSystemService(Context.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;
}
}
报警接收器
[BroadcastReceiver]
class AlarmReciver : BroadcastReceiver, IAlarmReceiver
{
public override void OnReceive(Context context, Intent intent)
{
var id = intent.GetStringExtra("jobId");
MessagingCenter.Send<IAlarmReceiver>(this, id);
}
}
[BroadcastReceiver(Enabled = true, Label = "Local Notifications Broadcast Receiver")]
class AlarmHandler : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
if (intent?.Extras != null)
{
string title = intent.GetStringExtra(View.AndroidNotificationManager.TitleKey);
string message = intent.GetStringExtra(View.AndroidNotificationManager.MessageKey);
View.AndroidNotificationManager manager = View.AndroidNotificationManager.Instance ?? new View.AndroidNotificationManager();
manager.Show(title, message, assignmentId);
}
}
public void SetAlarm(TimeSpan dueTime, TimeSpan interval, int jobId)
{
var alarmIntent = new Intent(Application.Context, typeof(AlarmReciver));
alarmIntent.PutExtra("jobId", jobId.ToString());
var pending = PendingIntent.GetBroadcast(Application.Context,
jobId, alarmIntent, PendingIntentFlags.UpdateCurrent);
var alarmManager = Application.Context.GetSystemService(Context.AlarmService)
.JavaCast<AlarmManager>();
alarmManager.SetRepeating(AlarmType.RtcWakeup, (long)dueTime.TotalMilliseconds,
(long)interval.TotalMilliseconds, pending);
}
public void UnsetAlarm(int jobId)
{
var alarmIntent = new Intent(Application.Context, typeof(AlarmReciver));
var pending = PendingIntent.GetBroadcast(Application.Context,
jobId, alarmIntent, PendingIntentFlags.UpdateCurrent);
var alarmManager = Application.Context.GetSystemService(Context.AlarmService)
.JavaCast<AlarmManager>();
alarmManager.Cancel(pending);
}
}
对于 iOS 它有点复杂。 IOS 提供用于刷新 non-critcal 内容的后台提取。无法选择间隔,因为 UIApplication.BackgroundFetchIntervalMinimum 取决于很多因素,例如应用程序使用情况、电池寿命等。您仍然希望使用 UIApplication.BackgroundFetchIntervalMinimum 来获取内容 尽可能多。 另一种方法是使用远程通知,使用 Apple 推送通知服务 (APN)。 此处的信息和示例 https://docs.microsoft.com/en-us/xamarin/ios/app-fundamentals/backgrounding/ios-backgrounding-techniques/updating-an-application-in-the-background.
两个平台的另一个选择是 Azure,如此处所述https://docs.microsoft.com/en-us/azure/developer/mobile-apps/notification-hubs-backend-service-xamarin-forms