8

How I can prevent background task from running if there are foreground app being executed? Using Universal Windows Platform for my app.

My background task is checking for new items on some site and sends a toast when there are new things available, but I wan't to prevent toast sending if user is running application right now.

I have tried to unregister task on my application launch and register it again when app is terminated, through, this is not fine solution for me -- when device shuts down for some reason (for example, battery was removed) my task will be not registered until app launched again.

Thanks.

6 Answers6

2

Use a named Mutex to synchronize between the foreground app and the background agent

0

I would be very interested to see a simpler solution. Barring that, you can put in place an arbitration mechanism. Something along these lines:

 public class AppSynchronization
 {
    // Called by the main app on launch or on resume. It will signal the main app's intention to start. 
    // The main app will not perform any significant actions before this method returns.
    public void ActivateMainApp() {...}

    // Called by the main app. It will signal the fact that the main app is "going away".
    public async Task MainAppSuspended() {...}

    // Called by the background agent.
    // It will signal the background agent intention to start. 
    // This method will only return if the main app is out of the way. 
    // It will return a cancellation token that will be used to cancel the activity of the background agent when the main app advertises its intention to start. 
    public async Task<CancellationToken> ActivateBackgroundAgent(CancellationToken cancelWait)
    {
        // Make sure the main app is not started or wait until the main app is out of the way 

        // Start a thread that is on the lookout for the main app announcing that it wants to start.
        // When that happens it will cancel the cancellation token returned. 
    }

    // <summary>
    // Called by the background agent. 
    // It will signal the fact that the background agent completed its actions. 
    public async Task DeactivateBackgroundAgent()
 }

In the main app:

private AppSynchronization appSynchronization;

public App()
{
    ...
    this.appSynchronization = new AppSynchronization();
}

protected async override void OnLaunched(LaunchActivatedEventArgs e)
{
    ... 
    if (rootFrame.Content == null)
    {
        // Advertise the fact that the main app wants to start. 
        // The background agent will know to cancel whatever its doing.
        // ActivateMainApp may have to be async although you need to make sure that OnLaunched supports that
        this.appSynchronization.ActivateMainApp();
        ...
    }
}

private async void OnResuming(object sender, object e)
{
    ...
    // Advertise the fact that the main app wants to resume.
    // The background agent will know to cancel whatever its doing.
    this.appSynchronization.ActivateMainApp();
}


private async void OnSuspending(object sender, SuspendingEventArgs e)
{
    var deferral = e.SuspendingOperation.GetDeferral();

    ...
    // Advertise the fact that the main app is suspending.
    // The background agent will know it is allowed to start doing work.
    await _mainAppSynchronization.MainAppSuspended();

    ...
    deferral.Complete();
}

private void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
    ...
    // Advertise the fact that the main app is going away.
    // The background agent will know it is allowed to start doing work.
    _mainAppSynchronization.MainAppSuspended().Wait();
}

And in the background agent:

public sealed class BackgroundTask : IBackgroundTask
{
    public async void Run(IBackgroundTaskInstance taskInstance)
    {
        ...
        AppSynchronization appSynchronization = new AppSynchronization();
        BackgroundTaskDeferral  deferral = taskInstance.GetDeferral();

        // Make sure that the main app is not started. If it is started then wait until the main app gets out of the way. 
        // It he main app is running this will wait indefinitely.
        // Use backgroundAgentCancellationToken to cancel the actions of the background agent when the main app advertises its intention to start.
        CancellationToken backgroundAgentCancellationToken = await appSynchronization.ActivateBackgroundAgent();

        await DoBackgroundAgentWork(backgroundAgentCancellationToken)

        // Advertise the fact that the background agent is out.
        // DeactivateBackgroundAgent will make sure that the synchronization mechanism advertised the fact that the background agent is out.
        // DeactivateBackgroundAgent may have to be declared async in case the synchronization mechanism uses async code to do what is needed.
        await appSynchronization.DeactivateBackgroundAgent();

        deferral.Complete();
    }

I am not sure if there is any way to communicate across processes in UWP. The arbitration mechanism itself may have to be based on files on the local storage.

The arbitration mechanism may have to include a heartbeat mechanism in the event that one or the other process crashes in a catastrophic way.

Ladi
  • 1,274
  • 9
  • 17
  • "If the main app is running this will wait indefinitely." (in the bg task). Are you sure about this? Aren't background tasks killed if they run too long? Or does the waiting not count as CPU usage? Would love to get some insights if possible. Thanks :) – sibbl Sep 27 '15 at 14:59
  • If the arbitration mechanism implemented in ActivateBackgroundAgent has a loop where it sleeps a few seconds then checks the state of the main app (maybe by examining the presence of a file that the main app is creating) then the CPU usage will be very small. The sleep time will not be counted. Eventually the background agent will be suspended and then you'll have to wait some time for the OS to resume it again. You will have to do a lot of tests around this though. As far as I know the background agent in Windows 8.1 vs. WP 8.1 had important differences. Not sure about UWP. – Ladi Sep 27 '15 at 20:39
  • In UWP if DeviceUseTrigger is not the trigger, the background task will cancel after 10 minutes in the case of an application trigger. This is causing me real pain with a GPS tracking app since SensorCore is now deprecated. – 27k1 May 02 '16 at 06:56
0

I have backgroundtask which is triggered by TimeTrigger. That task restarts automatically after phone restart without a need of loading app first. Maybe that would help...

[edited] just realized i'm being late with my answer...

gecharo
  • 51
  • 4
0

You can check the status of the App using below code

var value =  ApplicationSettingsHelper.ReadResetSettingsValue(ApplicationSettingsConstants.AppState);
if (value == null)
     foregroundAppState = AppState.Unknown;
else
     foregroundAppState = EnumHelper.Parse<AppState>(value.ToString());

if (foregroundAppState == AppState.Suspended)
      //Do something
else if (foregroundAppState == AppState.Active)
      return;
else if (foregroundAppState == AppState.Unknown)
      return;
Sushil_Arora
  • 58
  • 1
  • 8
0

The way to do this is to use Application Local Settings.

Set the setting in foreground and background. As an example in foreground do this.

/// <summary>
/// When the window visibility changes
/// </summary>
/// <param name="sender">object sender</param>
/// <param name="e">VisibilityChangedEventArgs e</param>
private void OnVisibilityChanged(object sender, VisibilityChangedEventArgs e)
{
    var localSettings = ApplicationData.Current.LocalSettings;

    if (!e.Visible)
    {
        localSettings.Values["AppInForeground"] = false;               
    }
    else
    {
        localSettings.Values["AppInForeground"] = true;
    }
}

Unfortunately sibbi is correct, if you are not using the DeviceUseTrigger your background task will cancel after 10 minutes.

27k1
  • 2,212
  • 1
  • 22
  • 22
0

This is similar to this question: How to stop the background task if the app is starting?

The easiest way would be to convert your tasks to in-proc background tasks: https://learn.microsoft.com/en-us/windows/uwp/launch-resume/convert-out-of-process-background-task

With an in-proc task you can check whether the app is in the foreground or not directly from the background activity.

If you need to keep your background tasks as separate processes from your main app, then you can do one of the following:

  • Create a Mutex via a file.
  • Create an in-proc app service and attempt to communicate to it from your background task. Then you can return a value whether the app is in the foreground.
  • Use the App Local Settings Dictionary to hold a flag about whether the foreground app is running. This is the most fragile, as an app crash could cause the flag to not be reset correctly.
chcortes
  • 116
  • 4