Xamarin forms Foreground service is stopping automatically

Manikishore A 6 Reputation points
2021-07-27T12:58:24.733+00:00

Hi Everyone,

I'm pretty new to Xamarin Development.
And my work is to track the user's location for every X minutes between User Sign in and Sign out. So, I opted for the Foreground service. And in few of mobiles, it is running fine. But, In few mobiles the Foreground service is getting stopped automatically.

And have selected the Battery optimization as NO RESTRICTIONS. Still it is getting stopped automatically.

Any other conditions I can for this reason?

Here is my Foreground Service Code

[Service]
public class AndroidLocationService : Service
{
    CancellationTokenSource _cts;
    public const int SERVICE_RUNNING_NOTIFICATION_ID = 10000;
    System.Timers.Timer aTimer;
    public static event EventHandler<string> onNotificationRaised;

    public override IBinder OnBind(Intent intent)
    {
        return null;
    }

    public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
    {
        _cts = new CancellationTokenSource();

        Notification notif = DependencyService.Get<INotification>().ReturnNotif();
        StartForeground(SERVICE_RUNNING_NOTIFICATION_ID, notif);

        LocationTrackBL trackBL = new LocationTrackBL();
        var freq = trackBL.GetTrackFrequency();

        Utilities.Log("Foreground Created: " + freq);

        aTimer = new System.Timers.Timer();
        aTimer.Elapsed += new ElapsedEventHandler(GetLocation);
        aTimer.Interval = freq * 60000;
        aTimer.Enabled = true;
        aTimer.Start();

        Utilities.Log("Timer Started");

        return StartCommandResult.Sticky;
    }

    public async void GetLocation(object source, ElapsedEventArgs e)
    {
        Utilities.Log("Location Fired");
        LocationTrackBL trackBL = new LocationTrackBL();
        try
        {
            bool signout = trackBL.IsManualSignOutHappened_Valid();
            if (signout)
            {
                aTimer.Enabled = false;
                aTimer.Stop();
                return;
            }

            bool signin = trackBL.IsManualSignInHappened_Valid();

            var freq = trackBL.GetTrackFrequency();
            DateTime tomorrow = DateTime.Now.Date.AddDays(1);
            TimeSpan difference = tomorrow - DateTime.Now;
            Utilities.Log("Time Difference: " + difference.TotalMinutes);

            if (difference.TotalMinutes < freq || !signin)
            {
                Utilities.Log("Timer stopped!!" + freq + " ||" + signin.ToString());
                aTimer.Enabled = false;
                aTimer.Stop();

                //stop the Foreground service
                var actname = CrossCurrentActivity.Current.Activity.LocalClassName;
                if (actname.Contains("SignInActivity"))
                {
                    (CrossCurrentActivity.Current.Activity as SignInActivity).StopService_ForFG();
                }
                return;
            }

            bool gpsStatus = DependencyService.Get<ILocSettings>().isGpsAvailable();
            if (!gpsStatus)
            {
                LocationTrack log = new LocationTrack();
                log.TrackDate = DateTime.Now;
                log.IsManual = false;
                log.Latitude = "0.0";
                log.Longitude = "0.0";
                log.IsSync = false;
                log.DayInOut = false;
                log.Image = "";
                log.InOut = 0;
                log.LocationName = "";
                log.LocationCode = "";
                log.LocationName = "";
                log.LocationType = 0;
                log.OffsiteReason = "GPS is not Enabled.";
                log.SignType = 0;
                trackBL.SaveLocationTrack(log);

                Utilities.Log("GPS not available");
                onNotificationRaised?.Invoke(null, "Enable GPS to track the Location%SignIn");
                return;
            }

            var request = new GeolocationRequest(GeolocationAccuracy.High, TimeSpan.FromSeconds(1));
            var cts = new CancellationTokenSource();
            var location = await Geolocation.GetLocationAsync(request, cts.Token);

            if (location == null)
            {
                var locator = CrossGeolocator.Current;
                var position = await locator.GetPositionAsync(TimeSpan.FromSeconds(5), null, true);

                if (position != null)
                {
                    location = new Xamarin.Essentials.Location();
                    location.Accuracy = position.Accuracy;
                    location.Latitude = position.Latitude;
                    location.Longitude = position.Longitude;
                    location.Altitude = position.Altitude;
                }
            }

            if (location != null)
            {
                LocationTrack log = new LocationTrack();
                log.TrackDate = DateTime.Now;
                log.IsManual = false;
                log.Latitude = location.Latitude.ToString();
                log.Longitude = location.Longitude.ToString();
                log.IsSync = false;
                log.DayInOut = false;
                log.Image = "";
                log.InOut = 0;
                log.LocationName = "";

                var currentloc = trackBL.SaveBackgroundLocationTrack(log, location.Accuracy ?? 0);
                trackBL.SyncPendingTracks();

                Device.BeginInvokeOnMainThread(() =>
                {
                    MessagingCenter.Send<object, string>(this, "CurrentLocation", currentloc + "$^$" + location.Latitude + ", " + location.Longitude);
                });

                Console.WriteLine($"Latitude: {location.Latitude}, Longitude: {location.Longitude}, Altitude: {location.Altitude}");

                Utilities.Log("CurrentLocation" + currentloc);
            }
            else
            {
                LocationTrack log = new LocationTrack();
                log.TrackDate = DateTime.Now;
                log.IsManual = false;
                log.Latitude = "0.0";
                log.Longitude = "0.0";
                log.IsSync = false;
                log.DayInOut = false;
                log.Image = "";
                log.InOut = 0;
                log.LocationName = "";
                log.LocationCode = "";
                log.LocationName = "";
                log.LocationType = 0;
                log.OffsiteReason = "GPS is not Enabled for Always.";
                log.SignType = 0;
                trackBL.SaveLocationTrack(log);

                Utilities.Log("Location Null");
            }
        }
        catch (FeatureNotSupportedException fnsEx)
        {
            Utilities.Log("FG Destroyed 1");
            // Handle not supported on device exception
        }
        catch (FeatureNotEnabledException fneEx)
        {
            Utilities.Log("FG Destroyed2");
            // Handle not enabled on device exception
        }
        catch (PermissionException pEx)
        {
            Utilities.Log("FG Destroyed3");
            // Handle permission exception
        }
        catch (Exception ex)
        {
            LocationTrack log = new LocationTrack();
            log.TrackDate = DateTime.Now;
            log.IsManual = false;
            log.Latitude = "0.0";
            log.Longitude = "0.0";
            log.IsSync = false;
            log.DayInOut = false;
            log.Image = "";
            log.InOut = 0;
            log.LocationName = "";
            log.LocationCode = "";
            log.LocationName = "";
            log.LocationType = 0;
            log.OffsiteReason = "GPS is not Enabled for Always.";
            log.SignType = 0;
            trackBL.SaveLocationTrack(log);

            Utilities.Log("Location Null");

            Utilities.Log("FG Destroyed4");
            Utilities.Log(ex.ToString());
            // Unable to get location
        }
    }

    public override void OnDestroy()
    {
        Utilities.Log("FG Destroyed");
        if (_cts != null)
        {
            _cts.Token.ThrowIfCancellationRequested();
            _cts.Cancel();
        }
        base.OnDestroy();
    }
}

Is there any way how to check whether the Service is running or not?

Thanks in Advance.

Xamarin
Xamarin
A Microsoft open-source app platform for building Android and iOS apps with .NET and C#.
5,311 questions
{count} vote

2 answers

Sort by: Most helpful
  1. JarvanZhang 23,951 Reputation points
    2021-07-28T03:36:16.13+00:00

    Hello,​

    Welcome to our Microsoft Q&A platform!

    Try using the Handler with an Action to keep doing the work in a foreground service. Here is the sample code, you could refer to:

       [Service(Enabled = true)]  
       public class TestService : 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(Application.MainLooper);  
               runnable = new Action(() =>  
               {  
                   if (IsStarted)  
                   {  
                       functionMethod();  
                       handler.PostDelayed(runnable, DELAY_BETWEEN_LOG_MESSAGES);  
                   }  
               });  
           }  
         
           private void functionMethod()  
           {  
               //  
           }  
         
           public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)  
           {  
         
               if (IsStarted)  
               {  
                   //service is already started  
               }  
               else  
               {  
                   CreateNotificationChannel();  
                   DispatchNotification_RegisterService();//register a service as a foreground service  
                   handler.PostDelayed(runnable, DELAY_BETWEEN_LOG_MESSAGES);  
                   IsStarted = true;  
               }  
               return StartCommandResult.Sticky;  
           }  
         
           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();  
           }  
         
           public override IBinder OnBind(Intent intent)  
           {  
               return null;  
           }  
         
           private void DispatchNotification_RegisterService()  
           {  
               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_btn_radio_material)  
                      .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_btn_radio_material)  
                      .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 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);  
           }  
       }  
    

    Sample code: https://github.com/xamarin/monodroid-samples/blob/master/ApplicationFundamentals/ServiceSamples/ForegroundServiceDemo/TimestampService.cs

    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.


  2. Matic Di Batista 21 Reputation points
    2022-06-10T20:39:13.397+00:00

    Hi,
    I have a similar issue. I prepared an example project: https://github.com/MaticDiba/XFBackgroundLocationSampleExtemded

    I tried deploying this project to my phone (Samsung Galaxy S20) via VisualStudio 2022. When I test this application with the screen off, it stops recording location after 2-3 minutes.

    When I run the application on my phone in debug mode it actually works. But if I disconnect my phone and then run the application, this issue happens: when putting the application to the background and then turning off the screen, it's logging locations for 2-3 minutes then it stops. When I resume the application it continues.

    I have other tracking applications like LocusMap, and there it works. So I guess the phone is not a problem.

    0 comments No comments