Windows 10 UWP - 当前应用正在运行时停止后台任务

8
我该如何防止后台任务在前台应用程序正在运行时执行?我使用通用Windows平台开发应用程序。
我的后台任务是检查某个站点上的新项目,并在有新可用事项时发送提示,但如果用户正在运行应用程序,则希望防止发送提示。
我尝试在应用程序启动时取消注册任务,并在应用程序终止时重新注册它,但这对我来说不是最好的解决方案--当设备由于某种原因(例如,电池被取出)关闭时,我的任务将不再注册,直到再次启动应用程序。
谢谢。

1
你找到解决方案了吗?你正在使用应用触发器任务吗? - 27k1
6个回答

2
使用命名互斥体在前台应用程序和后台代理之间进行同步。

0

我有一个后台任务,它是由时间触发器触发的。这个任务在手机重启后会自动重新启动,而无需首先加载应用程序。也许这会有所帮助...

[编辑]刚才意识到我的回答有点晚了...


0

这类似于这个问题:如何在应用程序启动时停止后台任务?

最简单的方法是将您的任务转换为进程内后台任务:https://learn.microsoft.com/zh-cn/windows/uwp/launch-resume/convert-out-of-process-background-task

使用进程内任务,您可以直接从后台活动中检查应用程序是否在前景中。

如果您需要将后台任务保持为与主要应用程序分离的进程,则可以执行以下操作之一:

  • 通过文件创建互斥体。
  • 创建一个进程内应用程序服务,并尝试从后台任务通信到该服务。然后,您可以返回一个值,指示应用程序是否在前景中。
  • 使用应用程序本地设置字典来保存有关前台应用程序是否正在运行的标志。这是最脆弱的一种方式,因为应用程序崩溃可能会导致无法正确重置标志。

0
您可以使用以下代码检查应用程序的状态。
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;

0

实现这个的方法是使用应用程序本地设置。

在前台和后台设置该设置。 例如,在前台执行以下操作。

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

很遗憾,sibbi是正确的,如果你没用到DeviceUseTrigger,你的后台任务将在10分钟后被取消。

0

我非常希望看到一个更简单的解决方案。如果没有,您可以实施一个仲裁机制。大致如下:

 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()
 }

在主应用程序中:
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();
}

而在后台代理中:

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();
    }

我不确定在UWP中是否有任何跨进程通信的方法。仲裁机制本身可能必须基于本地存储上的文件。

仲裁机制可能还必须包括一个心跳机制,以防其中一个进程以灾难性的方式崩溃。


如果主应用程序正在运行,则此代码将无限期等待(在后台任务中)。您确定吗?如果后台任务运行时间过长,它们不会被杀死吗?或者等待不算作CPU使用时间?如果可能的话,希望能够获得一些见解。谢谢 :) - sibbl
如果ActivateBackgroundAgent中实现的仲裁机制有一个循环,在其中休眠几秒钟,然后检查主应用程序的状态(可能通过检查主应用程序正在创建的文件的存在)那么CPU使用率将非常小。睡眠时间不会被计算。最终,后台代理将被暂停,然后您将不得不等待一段时间才能再次恢复它。不过,您需要围绕此进行大量测试。据我所知,Windows 8.1与WP 8.1中的后台代理存在重要差异。不确定UWP是否也有这些差异。 - Ladi
在UWP中,如果DeviceUseTrigger不是触发器,则在应用程序触发器的情况下,后台任务将在10分钟后取消。这使我在GPS跟踪应用程序中感到真正的痛苦,因为SensorCore现在已被弃用。 - 27k1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接