Reputation: 93
We are having some trouble getting a background service working. Even when the app is closed and the phone is locked, a timer should execute some code once per second. This works fine as long as the app is open or in background and the phone is in use, however when the phone is locked and in standby, the service stops automatically after some time.
The code is modeled after this example: http://arteksoftware.com/backgrounding-with-xamarin-forms/
This code is executed in MainActivity:
MessagingCenter.Subscribe<StartBackgroundTimer>(this, "StartBackgroundTimer", message =>
{
var intent = new Intent(this, typeof(LongRunningTaskService));
StartService(intent);
});
MessagingCenter.Subscribe<StopBackgroundTimer>(this, "StopBackgroundTimer", message =>
{
var intent = new Intent(this, typeof(LongRunningTaskService));
StopService(intent);
});
This is the LongRunningTaskService class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using System.Threading;
using System.Threading.Tasks;
using OurAppNamespace.Classes;
using Xamarin.Forms;
namespace OurAppNamespace.Droid
{
[Service]
public class LongRunningTaskService : Service
{
CancellationTokenSource _cts;
public override IBinder OnBind(Intent intent)
{
return null;
}
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
_cts = new CancellationTokenSource();
Task.Run(() =>
{
try
{
//INVOKE THE SHARED CODE
var timer = new BackgroundTimer();
timer.RunBackgroundTimer(_cts.Token).Wait();
}
catch (System.OperationCanceledException)
{
}
finally
{
if (_cts.IsCancellationRequested)
{
var message = new BackgroundTimerCancelledMessage();
Device.BeginInvokeOnMainThread(
() => MessagingCenter.Send(message, "BackgroundTimerCancelledMessage")
);
}
StopSelf();
}
}, _cts.Token);
return StartCommandResult.Sticky;
}
public override void OnCreate()
{
base.OnCreate();
}
public override void OnDestroy()
{
if (_cts != null)
{
_cts.Token.ThrowIfCancellationRequested();
_cts.Cancel();
}
base.OnDestroy();
}
}
}
Then, in the PCL:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace OurAppNamespace.Classes
{
public class StartBackgroundTimer { }
public class StopBackgroundTimer { }
public class BackgroundTimerMessage
{
}
public class BackgroundTimerCancelledMessage { }
public class BackgroundTimer
{
public async Task RunBackgroundTimer(CancellationToken token)
{
await Task.Run(async () =>
{
while (true)
{
token.ThrowIfCancellationRequested();
await Task.Delay(1000);
Device.BeginInvokeOnMainThread(() =>
{
MessagingCenter.Send<BackgroundTimerMessage>(new BackgroundTimerMessage(), "BackgroundTimer");
});
}
}, token);
}
}
}
And finally, the code to be executed once the BackgroundTimer calls:
private static void UpdateActiveTimers()
{
MessagingCenter.Subscribe<BackgroundTimerMessage>(instance, "BackgroundTimer", message =>
{
(unrelated code)
DependencyService.Get<INotificationHandling>().UpdateTimerNotification(timeText);
}
});
}
When starting the timer for 10 minutes, there are different results with different phones.
A Xiaomi Redmi Note 3 Pro can execute this code with the application in background and the phone locked for 10 minutes (did not test it any longer).
The Blackberry Priv works fine for around 2 minutes with the app in background and the phone locked (and then going to standby), then the timer freezes / the notification does not get updated anymore until the phone gets turned on again (unlock is not needed).
The notification displays the remaining time of the timer. When the phone is locked, the timer seems to pause and resumes when the phone is unlocked again. When the phone is locked with 20 minutes remaining on the timer and is unlocked after 10 minutes, the timer resumes counting at about 18 minutes.
The HOMTOM HT16 shows the same behaviour as the Blackberry Priv but it pauses the timer even earlier after around 30 seconds.
How to change the code so that the background service runs successfully on all devices when the phone is locked and sleeping?
Thanks in advance!
Upvotes: 3
Views: 2883
Reputation: 93
I'd like to thank SushiHangover for his comment, using a Foreground Service (-> promoting the existing service to a foreground service) solved the issue.
Upvotes: 2
Reputation: 151
Your problem may be related to the new "Doze" mode in Android (API 23+). It messes up all background tasks. Please see this link: https://developer.android.com/training/monitoring-device-state/doze-standby.html
Upvotes: 1