如何在UWP应用中使用后台任务

时间:2018-10-05 20:23:18

标签: c# uwp background-task

我正在开发一个UWP应用,该应用使用进程内后台任务来下载新的GitHub通知并将其显示为吐司通知。我正在使用MVVM灯。我使用 TimeTrigger 尝试了进程内后台任务,当应用程序处于前台或最小化时,一切都按预期工作。但是,当应用程序关闭时,后台任务将停止工作。监视任务管理器后,我发现后台任务已启动,但启动后立即关闭。因此,我使用了 ExtendedExecutionSession 来延长暂停时间。仍然没有用。使用“进程外”后台任务可以解决先前的问题,但会引入新的问题。由于无法调用API所需的应用数据(GitHub Auth数据),导致GitHub api调用在后台失败。

简而言之,我遇到以下问题:

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

我找到了以下解决方法:

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

我的问题是:

  1. 在处理任务中如何以及何时调用 ExtendedExecutionSession

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

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

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

并且我覆盖了 App.xaml.cs 中的 OnBackgroundActivated 。这是代码spippet:

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

1 个答案:

答案 0 :(得分:0)

代码中的问题是您过早完成Deferral,即在异步调度程序工作项有机会执行之前。当应用程序位于前台时,此过程会起作用,因为该过程仍然会继续进行,但是在后台激活时,该过程将一直运行,直到您将延迟标记为完成,这意味着它将在您的任务代码有机会执行之前退出运行。

要了解我的意思,请尝试一些实验。在调试器下运行模仿您当前实现的代码,并观察中断点的执行顺序。这将帮助您意识到在实际代码运行之前,您正在调用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()调用移至代码中的某个位置。您可能需要为此更改'deferral'的范围,并创建一个类级变量。