Xamarin. Android Фоновая задача удаляется после закрытия приложения - PullRequest
0 голосов
/ 08 января 2020

Задача: Создать фоновую задачу для запуска при остановке / приостановке приложения, которая периодически (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. вернуть STICKY в OnStartCommand
  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-запросов и не работает с сущностями, db connection et c, он просто отправляет новое уведомление каждый X секунд. Что я должен увидеть, так это то, что когда приложение закрывается, служба запускается и создается собственное уведомление, что я и вижу. Затем в течение некоторого времени я вижу, что генерируется уведомление «Тревога», а затем мое сервисное уведомление уничтожается, сервис удаляется, и это все. Если я нажимаю кнопку питания на телефоне, чтобы включить экран, я вижу, что уведомления о тревоге снова начинают действовать. Я проверил на нескольких мобильных устройствах с разными Android ОС (6, 7 и 8) и с отключенными режимами энергосбережения, без разницы, сервисное уведомление убито. В чем проблема, что я делаю не так?

Заранее спасибо за любую помощь или руководство!

1 Ответ

1 голос
/ 08 января 2020

Я думаю, что вы используете 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());

}
...