xamarin - Xamarin.Android 应用程序关闭后处理的后台任务
问题描述
任务:创建一个后台任务以在应用程序停止/暂停时运行,它会定期(3-7 秒)执行 HTTP 请求并将响应信息存储在 mysqlite 中,并在需要时显示本地通知。
我创建了如下后台服务,
[Service(Enabled = true)]
public class MyRequestService : Service
这就像 MainActivity 的意图一样开始,
public void StartMyRequestService()
{
var serviceToStart = new Intent(this, typeof(MyRequestService));
StartService(serviceToStart);
}
public void StopMyRequestService()
{
var serviceToStart = new Intent(this, typeof(MyRequestService));
StopService(serviceToStart);
}
protected override void OnPause()
{
base.OnPause();
StartMyRequestService();
}
protected override void OnDestroy()
{
base.OnDestroy();
StartMyRequestService();
}
protected override void OnResume()
{
base.OnResume();
StopMyRequestService();
}
在我的服务中,我使用了以下功能,
- 在 OnStartCommand 中返回 STICKY
- 创建一个“永久的”本地人
- 带频道的通知
- 电源管理器锁
代码如下所示,
private Handler handler;
private Action runnable;
private bool isStarted
private WakeLock wakeLock;
public override void OnCreate()
{
base.OnCreate();
handler = new Handler();
runnable = new Action(() =>
{
DispatchNotificationThatAlarmIsGenerated("I'm running");
handler.PostDelayed(runnable, DELAY_BETWEEN_LOG_MESSAGES);
});
}
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
if (isStarted)
{
// service is already started
}
else
{
CreateNotificationChannel();
DispatchNotificationThatServiceIsRunning();
handler.PostDelayed(runnable, DELAY_BETWEEN_LOG_MESSAGES);
isStarted = true;
PowerManager powerManager = (PowerManager)this.GetSystemService(Context.PowerService);
WakeLock wakeLock = powerManager.NewWakeLock(WakeLockFlags.Full, "Client Lock");
wakeLock.Acquire();
}
return StartCommandResult.Sticky;
}
public override void OnTaskRemoved(Intent rootIntent)
{
//base.OnTaskRemoved(rootIntent);
}
public override IBinder OnBind(Intent intent)
{
// Return null because this is a pure started service. A hybrid service would return a binder that would
// allow access to the GetFormattedStamp() method.
return null;
}
public override void OnDestroy()
{
// Stop the handler.
handler.RemoveCallbacks(runnable);
// Remove the notification from the status bar.
var notificationManager = (NotificationManager)GetSystemService(NotificationService);
notificationManager.Cancel(NOTIFICATION_SERVICE_ID);
isStarted = false;
wakeLock.Release();
base.OnDestroy();
}
private void CreateNotificationChannel()
{
//Notification Channel
NotificationChannel notificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_NAME, NotificationImportance.Max);
notificationChannel.EnableLights(true);
notificationChannel.LightColor = Color.Red;
notificationChannel.EnableVibration(true);
notificationChannel.SetVibrationPattern(new long[] { 100, 200, 300, 400, 500, 400, 300, 200, 400 });
NotificationManager notificationManager = (NotificationManager)this.GetSystemService(Context.NotificationService);
notificationManager.CreateNotificationChannel(notificationChannel);
}
private void DispatchNotificationThatServiceIsRunning()
{
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
.SetDefaults((int)NotificationDefaults.All)
.SetSmallIcon(Resource.Drawable.icon)
.SetVibrate(new long[] { 100, 200, 300, 400, 500, 400, 300, 200, 400 })
.SetSound(null)
.SetChannelId(NOTIFICATION_CHANNEL_ID)
.SetPriority(NotificationCompat.PriorityDefault)
.SetAutoCancel(false)
.SetContentTitle("Mobile")
.SetContentText("My service started")
.SetOngoing(true);
NotificationManagerCompat notificationManager = NotificationManagerCompat.From(this);
notificationManager.Notify(NOTIFICATION_SERVICE_ID, builder.Build());
}
private void DispatchNotificationThatAlarmIsGenerated(string message)
{
var intent = new Intent(this, typeof(MainActivity));
intent.AddFlags(ActivityFlags.ClearTop);
var pendingIntent = PendingIntent.GetActivity(this, 0, intent, PendingIntentFlags.OneShot);
Notification.Builder notificationBuilder = new Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
.SetSmallIcon(Resource.Drawable.icon_round)
.SetContentTitle("Alarm")
.SetContentText(message)
.SetAutoCancel(true)
.SetContentIntent(pendingIntent);
var notificationManager = (NotificationManager)GetSystemService(NotificationService);
notificationManager.Notify(App.NOTIFICATION_ALARM, notificationBuilder.Build());
}
这只是一个示例,代码不发出任何 HTTP 请求,也不与实体、数据库连接等一起工作,它只是每 X 秒发送一个新通知。我应该看到的是,当应用程序关闭时,服务启动并创建本机通知,这就是我所看到的。然后有一段时间我看到正在生成“警报”通知,然后我的服务通知被终止,服务被处理,仅此而已。如果我单击手机上的电源按钮以点亮屏幕,我会看到“警报”通知再次出现。我已经检查了几台具有不同 Android 操作系统(6、7 和 8)并且禁用了省电模式的移动设备,没有区别,服务通知被终止。有什么问题,我做错了什么?
提前感谢您的任何帮助或指导!
解决方案
我认为您正在使用Foreground Services。
您应该通过 StartForeground 方法发送服务通知(前台通知)。
所以试着改变
private void DispatchNotificationThatServiceIsRunning()
{
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
.SetDefaults((int)NotificationDefaults.All)
.SetSmallIcon(Resource.Drawable.icon)
.SetVibrate(new long[] { 100, 200, 300, 400, 500, 400, 300, 200, 400 })
.SetSound(null)
.SetChannelId(NOTIFICATION_CHANNEL_ID)
.SetPriority(NotificationCompat.PriorityDefault)
.SetAutoCancel(false)
.SetContentTitle("Mobile")
.SetContentText("My service started")
.SetOngoing(true);
NotificationManagerCompat notificationManager = NotificationManagerCompat.From(this);
notificationManager.Notify(NOTIFICATION_SERVICE_ID, builder.Build());
}
到
private void DispatchNotificationThatServiceIsRunning()
{
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
.SetDefaults((int)NotificationDefaults.All)
.SetSmallIcon(Resource.Drawable.icon)
.SetVibrate(new long[] { 100, 200, 300, 400, 500, 400, 300, 200, 400 })
.SetSound(null)
.SetChannelId(NOTIFICATION_CHANNEL_ID)
.SetPriority(NotificationCompat.PriorityDefault)
.SetAutoCancel(false)
.SetContentTitle("Mobile")
.SetContentText("My service started")
.SetOngoing(true);
NotificationManagerCompat notificationManager = NotificationManagerCompat.From(this);
//dispatch foreground notification
StartForeground(NOTIFICATION_SERVICE_ID, builder.Build());
}
推荐阅读
- qt - 如何在 QTreeView 中显示所有子项?
- java - 获取两个位置 android google maps 之间的距离并画线
- html - 生成的表格比编码的小
- .net - 在 .Net 中使用带有 X509 的证书吊销列表文件
- python - 用于 pygame 的 Ubuntu systemd 服务
- android - 为android创建自定义显示活动
- python-3.x - 代码第二次运行失败,但在删除 _pycache_ 文件夹后运行?
- batch-file - 使用批处理文件获取 httpresponse 的值
- firebase-authentication - 私有服务器的 Firebase 身份验证
- ios - 使用 Kingfisher ios 在同一 url 中显示更新的图像