Xamarin Background Tasks

Background Tasks are a way to run a task, in the background, outside the lifecycle of your mobile app. This is different than running in a background thread, in some cases, it is running in a completely different process. Background tasks enable you to run code at specific intervals, or have tasks continue to run, even if the app is closed, which is useful in such situations as a large file download. Each platform has a different method of dealing with background tasks. We will also look into how to interact with them in Xamarin Forms.

I also created a GitHub repo BackgroundTasks, if you want to see some complete sample code.

Android Services

To run a background task in Android, you simply use a Service. Android generally classifies tasks as either Long Running Tasks or Periodic Tasks. Either of these, is reasonable grounds to create a Service. What type of Service you want to create, depends on your use, however for this example we will be looking at the Started Service. A Started Service, can be called via an external event or from an activity. This service will continue, regardless of the app state, until you tell it to stop.

[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;
    }
}

You can start this service in your Android code, as follows.

var intent = new Intent (this, typeof(PeriodicService));
StartService(intent);

Or you can use the Alarm Manager to periodically call it.

Alarm Manager

If we want a periodic background task, the Alarm Manager provides an easy way to trigger your service at specified intervals. To setup your app to be called periodically, you need to add in this code.

Create a BroadcastReceiver

[BroadcastReceiver]
public class BackgroundReceiver : BroadcastReceiver
{
    public override void OnReceive(Context context, Intent intent)
    {
        PowerManager pm = (PowerManager)context.GetSystemService(Context.PowerService);
        PowerManager.WakeLock wakeLock = pm.NewWakeLock(WakeLockFlags.Partial, "BackgroundReceiver");
        wakeLock.Acquire();

        // Run your code here

        wakeLock.Release();
    } 
}

iOS Backgrounding

Check out Xamarin’s documentation on iOS Backgrounding for more details about each one. For this example I will be using Background Fetch.

First, enable Background Fetch. Go to Properties > iOS Application and Background Modes to enable Background Fetch.

Then in your AppDelegate.cs in FinishedLaunching add in the following line.

public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
    global::Xamarin.Forms.Forms.Init();
    LoadApplication(new App());

    UIApplication.SharedApplication.SetMinimumBackgroundFetchInterval(UIApplication.BackgroundFetchIntervalMinimum);

    return base.FinishedLaunching(app, options);
}

Then override PerformFetch to run code on each fetch.

public override void PerformFetch(UIApplication application, Action<UIBackgroundFetchResult> completionHandler)
{
    // Check for new data, and display it


    // Inform system of fetch results
    completionHandler(UIBackgroundFetchResult.NewData);
}

If you want to simulate a background fetch, you currently can’t do this through Visual Studio on Windows. You will need to do it via the Mac Build Host.

UWP Background Tasks

For background tasks in UWP, we need to create a new runtime component.

Add a reference to this new component from your UWP app.

Add a background task to your component.

public sealed class ExampleBackgroundTask : IBackgroundTask
{
    BackgroundTaskDeferral _deferral;
    public void Run(IBackgroundTaskInstance taskInstance)
    {
        _deferral = taskInstance.GetDeferral();
 
        // Run your background task code here

        _deferral.Complete();
    }
}

Now, we need to make sure we register the Background Task in the appxmanifest.

Finally, we need to trigger the background task. In your UWP app, create and register the background task.

private async Task BackgroundTask()
{

    BackgroundExecutionManager.RemoveAccess();

    await BackgroundExecutionManager.RequestAccessAsync();

    var builder = new BackgroundTaskBuilder();

    builder.Name = "BackgroundTask";
    builder.TaskEntryPoint = "UWPRuntimeComponent.BackgroundTask";
    builder.SetTrigger(new SystemTrigger(SystemTriggerType.InternetAvailable, false));

    BackgroundTaskRegistration task = builder.Register();

    task.Completed += Task_Completed;
}

private void Task_Completed(BackgroundTaskRegistration sender, BackgroundTaskCompletedEventArgs args)
{
    var settings = Windows.Storage.ApplicationData.Current.LocalSettings;
    var key = "BackgroundTask";
    var message = settings.Values[key].ToString();

    // Run your background task code here
    MessagingCenter.Send<object, string>(this, "UpdateLabel", message);
}

You also should check if it is registered, before trying to register it again, and deregistering it, if appropriate. You can do this with the following code.

private void Deregister()
{
    var taskName = "BackgroundTask";

    foreach (var task in BackgroundTaskRegistration.AllTasks)
        if (task.Value.Name == taskName)
            task.Value.Unregister(true);
}

private bool IsRegistered()
{
    var taskName = "BackgroundTask";

    foreach (var task in BackgroundTaskRegistration.AllTasks)
        if (task.Value.Name == taskName)
            return true;

 return false;
}

Xamarin Forms Integration

As you have just read, each platform is quite different in how it handles background tasks. If you want to call or integrate with Xamarin Forms, we will need to create a certain level of abstraction to start the service if needed and receive messages back.

Using an interface such as below, you can inject this into your Xamarin Forms application to start the service when needed.

public interface IBackgroundService
{
    void Start();
}

However, this will also be one of the very rare occasions, where I would recommend MessagingCenter as a possible alternative. But only when receiving information back, not for starting it. For example, from within the Service, AppDelegate or BackgroundTask add this code.

MessagingCenter.Send<object, string>(this, "UpdateLabel", "Hello from [Platform]");

And subscribe to it from within your Xamarin Forms application.

MessagingCenter.Subscribe<object, string>(this, "UpdateLabel", (s,e) => { 
    Device.BeginInvokeOnMainThread(() =>
    {
        BackgroundServiceLabel.Text = e;
    });
});

Resource Conflicts

As a word of caution, be aware of what service or application is accessing an application, for example a SQLite DB. Two processes can not access a file at the same time, unless read-only. Hence ensure only one process is performing actions on file’s or locked resources.


Posted

in

by

Tags: