Xamarin AndroidX WorkManager not working when app is killed

Alessandro Severa 11 Reputation points
2021-07-02T12:32:53.493+00:00

I'm trying to setup a WorkManager that executes a task every 15 minutes. For now I'm doing this test adding an entry in a firebase database.

I'm using the nuget package Xamarin.AndroidX.Work.Runtime (2.5.0.2)

This works fine if the app is in foreground or background, but if I kill the app it stops working.

I'm doing this test with a Nokia 8 Sirocco (api 28) and with a Samsung Galaxy A21S (api 30) that both mount Android Stock.

This is how I launch the work:

private void StartParallelWorkManager(int minutes)
    {
        PeriodicWorkRequest taskLauncher = PeriodicWorkRequest.Builder.From<UniquePeriodicWorker>(TimeSpan.FromMinutes(minutes)).Build();
        WorkManager.GetInstance(Xamarin.Essentials.Platform.AppContext).EnqueueUniquePeriodicWork("TestTask", ExistingPeriodicWorkPolicy.Keep, taskLauncher);
    }

and this is my Worker class:

public class UniquePeriodicWorker : Worker
    {
        public UniquePeriodicWorker(Context context, WorkerParameters workerParameters) : base(context, workerParameters)
        {
        }

        public override Result DoWork()
        {

            System.Console.WriteLine("{0} DO WORK", DateTime.Now.ToString("dd/MM HH:mm:ss"));
            try
            {
                _ = CrossCloudFirestore.Current
                             .Instance
                             .Collection("WorkManagerRuns")
                             .AddAsync<DateTime>(DateTime.Now);
            }
            catch(Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex);
            }
            return Result.InvokeSuccess();
        }
    }

Doing the same test with native code in android studio works like a charm.

Am I missing something in Xamarin side or is Xamarin.AndroidX.Work.Runtime bugged?
Any suggestion?

Xamarin
Xamarin
A Microsoft open-source app platform for building Android and iOS apps with .NET and C#.
5,289 questions
0 comments No comments
{count} votes

1 answer

Sort by: Most helpful
  1. JarvanZhang 23,936 Reputation points
    2021-07-05T05:44:11.05+00:00

    Hello,​

    Welcome to our Microsoft Q&A platform!

    Starting in Android 8.0 (API level 26), an Android application no longer have the ability to run freely in the background. When an application moves into the background, Android will grant the app a certain amount of time to start and use services. Once that time has elapsed, the app can no longer start any services and any services that were started will be terminated. At this point it is not possible for the app to perform any work.

    To keep the service working even when the app is closed, try using a ForegroundService instead. Here is the sample code:

       [Service(Enabled = true)]  
       public class CustomService : Service  
       {  
           private Handler handler;  
           private Action runnable;  
           private bool isStarted;  
           private int DELAY_BETWEEN_LOG_MESSAGES = 5000;  
           private int NOTIFICATION_SERVICE_ID = 1001;  
           private int NOTIFICATION_AlARM_ID = 1002;  
           private string NOTIFICATION_CHANNEL_ID = "1003";  
           private string NOTIFICATION_CHANNEL_NAME = "MyChannel";  
           public override void OnCreate()  
           {  
               base.OnCreate();  
         
               handler = new Handler();  
               //here is what you want to do always  
               runnable = new Action(() =>  
               {  
                   if (isStarted)  
                   {  
                       DispatchNotificationThatAlarmIsGenerated("I'm running");  
                       //the function  
                       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;  
               }  
               return StartCommandResult.Sticky;  
           }  
         
           private void DispatchNotificationThatServiceIsRunning()  
           {  
               var notificationManager = (NotificationManager)GetSystemService(NotificationService);  
         
               if (Build.VERSION.SdkInt > BuildVersionCodes.O)  
               {  
                   var chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, GetString(Resource.String.abc_action_bar_home_description), NotificationImportance.Default);  
                   chan.LightColor = Android.Graphics.Color.Green;  
                   chan.LockscreenVisibility = NotificationVisibility.Private;  
         
                   notificationManager.CreateNotificationChannel(chan);  
         
                   NotificationCompat.Builder builder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)  
                      .SetAutoCancel(false)  
                      .SetSmallIcon(Resource.Drawable.abc)  
                      .SetContentTitle("Mobile")  
                      .SetContentText("My service started");  
                   StartForeground(NOTIFICATION_SERVICE_ID, builder.Build());  
               }  
               else  
               {  
                   NotificationCompat.Builder builder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)  
                      .SetDefaults((int)NotificationDefaults.All)  
                      .SetSmallIcon(Resource.Drawable.abc)  
                      .SetVibrate(new long[] { 100, 200, 300, 400, 500, 400, 300, 200, 400 })  
                      .SetSound(null)  
                      .SetPriority(NotificationCompat.PriorityDefault)  
                      .SetAutoCancel(false)  
                      .SetContentTitle("Mobile")  
                      .SetContentText("My service started")  
                      .SetOngoing(true);  
         
                   StartForeground(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);  
         
               var notificationManager = (NotificationManager)GetSystemService(NotificationService);  
         
               if (Build.VERSION.SdkInt > BuildVersionCodes.O)  
               {  
                   NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, "default")  
                   .SetContentTitle("Alarm")  
                   .SetContentText(message)  
                   .SetSmallIcon(Resource.Drawable.abc)  
                   .SetAutoCancel(true);  
         
                   var chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, GetString(Resource.String.abc_action_bar_home_description), NotificationImportance.Default);  
                   chan.LightColor = Android.Graphics.Color.Green;  
                   chan.LockscreenVisibility = NotificationVisibility.Private;  
         
                   notificationManager.CreateNotificationChannel(chan);  
                   notificationManager.Notify(NOTIFICATION_AlARM_ID, notificationBuilder.Build());  
               }  
               else  
               {  
                   NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)  
                      .SetDefaults((int)NotificationDefaults.All)  
                      .SetSmallIcon(Resource.Drawable.abc)  
                      .SetVibrate(new long[] { 100, 200, 300, 400, 500, 400, 300, 200, 400 })  
                      .SetSound(null)  
                      .SetPriority(NotificationCompat.PriorityDefault)  
                      .SetAutoCancel(false)  
                      .SetContentTitle("Mobile")  
                      .SetContentText("My service started")  
                      .SetOngoing(true);  
         
                   notificationManager.Notify(NOTIFICATION_AlARM_ID, notificationBuilder.Build());  
               }  
           }  
         
           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;  
               base.OnDestroy();  
           }  
         
           private void CreateNotificationChannel()  
           {  
               //Notification Channel  
               NotificationChannel notificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_NAME, NotificationImportance.Max);  
               notificationChannel.EnableLights(true);  
               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);  
           }  
       }  
    

    Check the doc: https://learn.microsoft.com/en-us/xamarin/android/app-fundamentals/services/foreground-services

    Best Regards,

    Jarvan Zhang


    If the response is helpful, please click "Accept Answer" and upvote it.

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.