How to run a method in the background only when app is open and running?
There are a few ways to do this in both iOS and Android. In Xamarin Forms most of this functionality falls under the moniker of Backgrounding. There are a lot of tutorials out there. This one is quite elaborate and definitely worth checking out:
http://arteksoftware.com/backgrounding-with-xamarin-forms/
In Android a lot of this work is done in a Background Service. For iOS look into Long Running or Finite-Length Tasks. As you can tell from this there is no Xamarin Forms way of doing this. You will need to write Xamarin.Android and Xamarin.iOS specific code.
What we did in our forms application was to make use of the Device.Timer and the Stopwatch class that available in System.Diagnostics, and Xamarin.Forms to create a very generic managed timer that we could interact with using the onStart, onSleep and onResume methods in Xamarin.Forms.
This particular solution doesn't require any special platform specific logic, and the device timer and stopwatch are non UI blocking.
using Xamarin.Forms;
using System;
using System.Linq;
using System.Diagnostics;
namespace YourNamespace
{
public partial class App : Application
{
private static Stopwatch stopWatch = new Stopwatch();
private const int defaultTimespan = 1;
protected override void OnStart()
{
// On start runs when your application launches from a closed state,
if (!stopWatch.IsRunning)
{
stopWatch.Start();
}
Device.StartTimer(new TimeSpan(0, 0, 1), () =>
{
// Logic for logging out if the device is inactive for a period of time.
if (stopWatch.IsRunning && stopWatch.Elapsed.Minutes >= defaultTimespan)
{
//prepare to perform your data pull here as we have hit the 1 minute mark
// Perform your long running operations here.
Device.InvokeOnMainThread(()=>{
// If you need to do anything with your UI, you need to wrap it in this.
});
stopwatch.Restart();
}
// Always return true as to keep our device timer running.
return true;
});
}
protected override void OnSleep()
{
// Ensure our stopwatch is reset so the elapsed time is 0.
stopWatch.Reset();
}
protected override void OnResume()
{
// App enters the foreground so start our stopwatch again.
stopWatch.Start();
}
}
}
Edit:
To give some context as to how the above solution works step by step:
The application starts from a closed state and the 'OnStart()' method creates our Device.Timer that ticks every second. It also starts our stopwatch that counts upto a minute.
When the app goes into the background it hits the 'OnSleep' method at this point if we were to pass a 'false' value into our Device.StartTimer() action it would not start up again. So instead we simply reset our stopwatch ready for when the app is opened again.
When the app comes back into the foreground it hits the 'OnResume' method, which simply starts the existing stopwatch.
2018 Edit:
This answer still has some merits even in 2018, but mainly for very specific situations. There are better platform specific ways to replicate this functionality even in Xamarin.Forms. The above still remains a platform agnostic way to perform a task after a period of time, taking into account user activity/inactivity.
To run a background task use a Service. Generally classifies tasks as either Long Running Tasks or Periodic Tasks.
The code for service in android look like this
[Service]
public class PeriodicService : Service
{
public override IBinder OnBind(Intent intent)
{
return null;
}
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
// From shared code or in your PCL
return StartCommandResult.NotSticky;
}
}
And to invoke the service in background
var intent = new Intent (this, typeof(PeriodicService));
StartService(intent);
In case wants to invoke and check after every minute
private void StartBackgroundDataRefreshService ()
{
var pt = new PeriodicTask.Builder ()
.SetPeriod (1800) // in seconds; minimum is 30 seconds
.SetService (Java.Lang.Class.FromType (typeof(BackgroundService)))
.SetRequiredNetwork (0)
.SetTag (your package name) // package name
.Build ();
GcmNetworkManager.GetInstance (this).Schedule (pt);
}
In order to know which service type is good for you read this tutorial Types of Services
Xamarin Blog for periodic background service Xamarin Service Blog
The other example is
public class PeriodicService : Service
{
private static Timer timer = new Timer();
public override IBinder OnBind(Intent intent)
{
return null;
}
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
timer.scheduleAtFixedRate(new mainTask(), 0, 5000);
return StartCommandResult.NotSticky;
}
private class mainTask extends TimerTask
{
public void run()
{
//your code
}
}
}
Here is Sample Code of XAMARIN Android Service Which will perform task after every 10 Seconds
using System;
using System.Threading;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Util;
namespace SimpleService
{
[Service]
public class SimpleStartedService : Service
{
static readonly string TAG = "X:" + typeof(SimpleStartedService).Name;
static readonly int TimerWait = 10000;
Timer timer;
DateTime startTime;
bool isStarted = false;
public override void OnCreate()
{
base.OnCreate();
}
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
Log.Debug(TAG, $"OnStartCommand called at {startTime}, flags={flags}, startid={startId}");
if (isStarted)
{
TimeSpan runtime = DateTime.UtcNow.Subtract(startTime);
Log.Debug(TAG, $"This service was already started, it's been running for {runtime:c}.");
}
else
{
startTime = DateTime.UtcNow;
Log.Debug(TAG, $"Starting the service, at {startTime}.");
timer = new Timer(HandleTimerCallback, startTime, 0, TimerWait);
isStarted = true;
}
return StartCommandResult.NotSticky;
}
public override IBinder OnBind(Intent intent)
{
// This is a started service, not a bound service, so we just return null.
return null;
}
public override void OnDestroy()
{
timer.Dispose();
timer = null;
isStarted = false;
TimeSpan runtime = DateTime.UtcNow.Subtract(startTime);
Log.Debug(TAG, $"Simple Service destroyed at {DateTime.UtcNow} after running for {runtime:c}.");
base.OnDestroy();
}
void HandleTimerCallback(object state)
{
TimeSpan runTime = DateTime.UtcNow.Subtract(startTime);
Log.Debug(TAG, $"This service has been running for {runTime:c} (since ${state})." );
}
}
}
you can use this,
System.Threading.Tasks.Task.Run(() =>
{
//Add your code here.
}).ConfigureAwait(false);