【问题标题】:Windows 10 UWP - stop background task if foreground app is runningWindows 10 UWP - 如果前台应用程序正在运行,则停止后台任务
【发布时间】:2015-12-24 16:11:08
【问题描述】:

如果有正在执行的前台应用程序,我如何防止后台任务运行?为我的应用程序使用通用 Windows 平台。

我的后台任务是检查某个站点上的新项目,并在有新内容可用时发送 toast,但如果用户现在正在运行应用程序,我不想阻止 toast 发送。

我试图在我的应用程序启动时取消注册任务并在应用程序终止时再次注册它,通过,这对我来说不是很好的解决方案 - 当设备由于某种原因关闭时(例如,电池被移除)我的任务在应用再次启动之前不会注册。

谢谢。

【问题讨论】:

  • 您找到解决方案了吗?你在使用应用触发任务吗?

标签: c# windows windows-phone uwp background-task


【解决方案1】:

使用命名的 Mutex 在前台应用程序和后台代理之间进行同步

【讨论】:

    【解决方案2】:

    我很想看到一个更简单的解决方案。除此之外,您可以建立仲裁机制。大致如下:

     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 中跨进程进行通信。仲裁机制本身可能必须基于本地存储上的文件。

    如果一个或另一个进程以灾难性方式崩溃,仲裁机制可能必须包括心跳机制。

    【讨论】:

    • "如果主应用程序正在运行,它将无限期地等待。" (在 bg 任务中)。你确定吗?如果后台任务运行时间过长,它们不会被杀死吗?或者等待不计入CPU使用率?如果可能的话,很想得到一些见解。谢谢:)
    • 如果在 ActivateBackgroundAgent 中实现的仲裁机制有一个循环,它会休眠几秒钟然后检查主应用程序的状态(可能通过检查主应用程序正在创建的文件的存在)然后CPU使用率将非常小。睡眠时间不计算在内。最终后台代理将被挂起,然后您将不得不等待一段时间让操作系统再次恢复它。不过,您将不得不对此进行大量测试。据我所知,Windows 8.1 与 WP 8.1 中的后台代理存在重要差异。不确定 UWP。
    • UWP中如果DeviceUseTrigger不是触发器,在应用程序触发的情况下,后台任务会在10分钟后取消。由于 SensorCore 现在已被弃用,这让我对 GPS 跟踪应用程序感到非常痛苦。
    【解决方案3】:

    我有由 TimeTrigger 触发的后台任务。该任务会在手机重启后自动重启,无需先加载应用程序。也许这会有所帮助...

    [已编辑] 刚刚意识到我的答案迟到了......

    【讨论】:

      【解决方案4】:

      您可以使用以下代码检查应用程序的状态

      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;
      

      【讨论】:

        【解决方案5】:

        执行此操作的方法是使用应用程序本地设置。

        设置前景和背景。 作为前台的示例,请执行此操作。

        /// <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 分钟后取消。

        【讨论】:

          【解决方案6】:

          这类似于这个问题:How to stop the background task if the app is starting?

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

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

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

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

          【讨论】:

            最近更新 更多