首页 > 解决方案 > 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();
}

在我的服务中,我使用了以下功能,

  1. 在 OnStartCommand 中返回 STICKY
  2. 创建一个“永久的”本地人
  3. 带频道的通知
  4. 电源管理器锁

代码如下所示,

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)并且禁用了省电模式的移动设备,没有区别,服务通知被终止。有什么问题,我做错了什么?

提前感谢您的任何帮助或指导!

标签: xamarinandroid-intentservicexamarin.androidbackground

解决方案


我认为您正在使用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());

}

推荐阅读