【问题标题】:How to work with background tasks in UWP apps如何在 UWP 应用中处理后台任务
【发布时间】:2018-10-07 17:38:04
【问题描述】:

我正在开发一个 UWP 应用程序,它使用进程内后台任务来下载新的 GitHub 通知并将它们显示为 toast 通知。我正在使用 MVVM 灯。我尝试使用 TimeTrigger 进行进程内后台任务,当应用程序处于前台或最小化时,一切都按预期工作。但是当应用程序关闭时,后台任务停止工作。监控任务管理器后,发现后台任务启动但刚启动就关闭了。所以我使用 ExtendedExecutionSession 来延长暂停时间。仍然无法正常工作。使用进程外后台任务解决了以前的问题,但引入了一个新问题。由于无法访问调用 API 所需的应用数据(GitHub Auth Data),GitHub api 调用在后台失败。

简而言之,我有以下问题:

  1. 当应用关闭时,进程中的后台任务不会在后台运行
  2. 当我使用 Octokit 对 GitHub 进行 API 调用时,进程外的后台任务可以共享应用程序数据(例如,在我的例子中是 GitHub Auth 数据)

我找到了以下解决方法:

  1. ExtendedExecutionSession 与正在进行的后台任务一起使用
  2. 在进程外后台任务中使用进程间通信

我的问题是:

  1. 我应该如何以及何时在 In-process 任务中调用 ExtendedExecutionSession

  2. 如何编写进程间通信以便能够从后台应用访问应用数据

请注意,我已在 MainPage.xaml.cs 中注册了后台任务。这是代码sn-p:

var syncBuilder = Helpers.BackgroundTaskHelper.BuildBackgroundTask(
                        "SyncNotifications",
                        new TimeTrigger(15, false),
                        internetAvailableCondition,
                        userPresentCondition,
                        sessionConnectedCondition
                   );
syncBuilder.IsNetworkRequested = true;            
syncBuilder.Register();

我覆盖了 App.xaml.cs 中的 OnBackgroundActivated。这是代码片段:

var deferral = args.TaskInstance.GetDeferral();
args.TaskInstance.Task.Completed += BackgroundTask_Completed;
var taskName = args.TaskInstance.Task.Name;
switch (taskName)
{
     case "ToastNotificationBackgroundTask":
          await CoreApplication
          .MainView
          .CoreWindow
          .Dispatcher
          .RunAsync(
               CoreDispatcherPriority.Normal, 
               async () =>
               {
                   var toastTriggerDetails = args.TaskInstance.TriggerDetails as ToastNotificationActionTriggerDetail;
                   var toastArgs = QueryString.Parse(toastTriggerDetails.Argument);
                        var notificationId = toastArgs["notificationId"];
                   if (!notificationId.IsNulloremptyOrNullorwhitespace())
                   {
                        await NotificationsService.MarkNotificationAsRead(notificationId);
                   }
               });
          break;
     case "SyncNotifications":
          await CoreApplication
          .MainView
          .CoreWindow
          .Dispatcher
          .RunAsync(
               CoreDispatcherPriority.Normal, 
               async () =>
               {                            
                    AppViewmodel.UnreadNotifications = await NotificationsService.GetAllNotificationsForCurrentUser(false, false);
                    await AppViewmodel.UnreadNotifications?.ShowToasts();
               });
          break;
 }

【问题讨论】:

  • 您是否检查过您的代码在前台应用未运行时是否崩溃?要记住的一件事是,您的应用在未运行时没有视图,因此如果您的后台任务代码假设有视图,它可能会失败/崩溃。特别是在您的代码中,我看到它取决于 CoreApplication.MainView - 在后台激活期间可能为空。
  • 另一件要检查的是你的延期。我看不到您何时/如何完成它。如果它只是在方法结束时超出范围,那么它将被标记为完成,并且任务将立即退出,在您的调度程序操作有机会运行完成之前。你需要坚持推迟,直到你真正完成你的任务。仔细想想,这很可能是您遇到的问题。
  • @StefanWickMSFT 编辑这篇文章是个错误。我实际上在代码末尾使用了deferral.Complete();
  • 我也试过不使用CoreApplication.MainView并得到相同的结果。
  • 请澄清您所说的“代码结束”是什么意思。您是指方法结束,还是在您实际完成任务之后?您还检查过您的代码是否在这里遇到异常/崩溃?

标签: c# uwp background-task


【解决方案1】:

您的代码中的问题是您过早地完成了延迟,即在您的异步调度程序工作项有机会执行之前。这恰好在应用程序处于前台时起作用,因为该进程无论如何都会保留,但是当在后台激活时,该进程只会运行直到您将延迟标记为完成,这意味着它会在您的任务代码有机会之前退出运行。

要明白我的意思,请尝试做一个小实验。在调试器下运行模拟您当前实现的代码,并观察断点被击中的顺序。这将帮助您在实际代码运行之前意识到您正在调用 Deferral.Complete:

protected async override void OnBgActivated()
{
    await CoreApplication.MainView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
    {
        await PerformTaskAsync();
        Debugger.Break();
    });
    Debugger.Break();
}

private async Task PerformTaskAsync()
{
    await Task.Yield();
    Debugger.Break();
    return;
}

现在您的解决方案很简单,在您的任务实际完成后,将 Deferral.Complete() 调用移至代码中的某个位置。您可能需要为此更改“延迟”的范围并创建一个类级变量。

【讨论】:

  • 首先,感谢您投入宝贵的时间。我尝试在 Task_Completed 方法中调用 _deferral.Complete(),因为我在重写的 OnBackroundActivated 方法中处理了 args.TaskInstance.Task.Completed 事件处理程序。结果是一样的。当应用关闭时,后台任务不再更新 Toast 通知。我还监控了 Windows 任务管理器和事件查看器,以了解后台任务是否无法运行,甚至是否在正确的时间运行。
  • 我发现当应用运行时,在我的应用下有一个名为Background Broker的子项在任务管理器中,我想它负责运行bg 任务,但是当我关闭应用程序时,一切都关闭了。也许每 15 分钟运行一次,但实际上什么也没做。另一方面,在进程之外的 bg 任务中,这不会发生。无论应用程序是否在运行,与进程外的bg任务,任务管理器中总是有后台代理。
  • 当应用程序打开时,我的应用程序在应用程序类别中列出子项:“MyAppName”和“后台代理”,当我关闭应用程序时,我进入任务管理器中的后台进程只有一个子项“后台经纪人”。在我看来,这意味着进程外的 bg 任务始终在后台运行,而进程内的 bg 任务在应用程序关闭时停止。否则,无论我在哪里调用_deferral.Complete()..,当应用程序处于运行状态时,我的进程内代码都会按预期工作
  • 您的代码中可能存在其他错误。如果没有看到问题的完整重现,这将很难诊断。我已经测试了这个场景,它按我的预期工作。这是我的测试项目供您参考:1drv.ms/u/s!AovTwKUMywTNq69gcwveq2EYSxG9Jg - 如果在查看我的代码后您的问题仍然存在,请分享您的完整代码以重现问题。
  • 你能告诉我们你最后选择的方向吗?我正在尝试做类似的事情,我获取数据但仍然怀疑仅 UWP 是否可以满足我的需求,因为它有 15 分钟的时间触发限制,并且如果我正确理解这个问题,使用后台进程并不简单
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-06-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-29
  • 1970-01-01
相关资源
最近更新 更多