Marvin's Blog【程式人生】

Ability will never catch up with the demand for it

04 Jul 2020

UWP文档笔记:幕后任务【二】

Create and register an in-process background task

即便App还在前台,同进程的幕后任务也可能被操作系统终结,如果它们运行时间超限的话。

1607新增了两个跟前后台切换相关的事件EnteredBackground和LeavingBackground 。可以通过EnteredBackground来启动需要在后台执行的代码。

和异进程幕后任务一样,可以通过BackgroundTaskBuilder来注册同进程的幕后任务,一点区别是同进程情况下不需要指定TaskEntryPoint,因为默认的加载点是OnBackgroundActivated。同进程幕后任务也不需要在app manifest里面声明。

注册幕后任务前同样必须调用RequestAccessAsync

可以用变量来追踪同进程幕后任务的进度。

同进程幕后任务也可以被中结,处理中结的时候,BackgroundActivated的事件处理器必须先退出才行,否则整个进程会被终结。

Create and register an out-of-process background task

本文介绍如何创建并注册一个异进程的幕后任务,这样可以在App切换到幕后的时候运行。

此幕后任务需要实现IBackgroundTask接口,这样一来,此幕后任务可以通过SystemTrigger或者MaintenanceTrigger触发。IBackgroundTask.Run 将是此任务的入口点。

另外,此幕后任务需要在单独的一个Windows Runtime Component项目中。

C#的话,幕后任务工程中的类必须是公开的,而且必须是sealed或者final的。

C++/WinRT的一个例子:

// First, add ExampleBackgroundTask.idl, and then build.
// ExampleBackgroundTask.idl
namespace Tasks
{
    [default_interface]
    runtimeclass ExampleBackgroundTask : Windows.ApplicationModel.Background.IBackgroundTask
    {
        ExampleBackgroundTask();
    }
}

// ExampleBackgroundTask.h
#pragma once

#include "ExampleBackgroundTask.g.h"


namespace winrt::Tasks::implementation
{
    struct ExampleBackgroundTask : ExampleBackgroundTaskT<ExampleBackgroundTask>
    {
        ExampleBackgroundTask() = default;

        void Run(Windows::ApplicationModel::Background::IBackgroundTaskInstance const& taskInstance);
    };
}

namespace winrt::Tasks::factory_implementation
{
    struct ExampleBackgroundTask : ExampleBackgroundTaskT<ExampleBackgroundTask, implementation::ExampleBackgroundTask>
    {
    };
}

// ExampleBackgroundTask.cpp
#include "pch.h"

#include "ExampleBackgroundTask.h"


namespace winrt::Tasks::implementation
{
    void ExampleBackgroundTask::Run(Windows::ApplicationModel::Background::IBackgroundTaskInstance const& taskInstance)
    {
        throw hresult_not_implemented();
    }
}

如果在幕后任务中运行异步任务,那么异步任务必须采用Deferral来延迟退出。

通过遍历BackgroundTaskRegistration.AllTasks可以判断异步任务是否已经注册。否则可能导致异步任务被注册多次。

std::wstring exampleTaskName{ L"ExampleBackgroundTask" };

auto allTasks{ Windows::ApplicationModel::Background::BackgroundTaskRegistration::AllTasks() };

bool taskRegistered{ false };
for (auto const& task : allTasks)
{
    if (task.Value().Name() == exampleTaskName)
    {
        taskRegistered = true;
        break;
    }
}

// The code in the next step goes here.

对于已注册的任务,可以通过 BackgroundTaskBuilder来创建一个实例:

if (!taskRegistered)
{
    Windows::ApplicationModel::Background::BackgroundTaskBuilder builder;
    builder.Name(exampleTaskName);
    builder.TaskEntryPoint(L"Tasks.ExampleBackgroundTask");
    builder.SetTrigger(Windows::ApplicationModel::Background::SystemTrigger{
        Windows::ApplicationModel::Background::SystemTriggerType::TimeZoneChange, false });
    // The code in the next step goes here.
}

builder还支持对trigger增加额外的触发条件:

builder.AddCondition(Windows::ApplicationModel::Background::SystemCondition{ Windows::ApplicationModel::Background::SystemConditionType::UserPresent });
// The code in the next step goes here.

通过调用builder的Register()方法来注册此任务

Windows::ApplicationModel::Background::BackgroundTaskRegistration task{ builder.Register() };

对于background trigger类型的幕后任务,在注册前必须先调用RequestAccessAsync 。

系统触发器ServicingComplete 可以用来执行App的升级后的自发更新,比如迁跃数据库还有更新幕后任务。最好先删除旧版本的幕后任务再添加新版本的。

为了获得幕后任务的处理结果,App需要注册回调到BackgroundTaskCompletedEventHandler。当App被加载或者恢复时,注册的回调会被调用,如果幕后任务完成了的话(如果你的App是在幕前,那么注册的回调会被直接执行)。

幕后任务必须在app manifest中声明。否则就会遇到"runtime class not registered” 错误。

幕后任务的声明在Package.appxmanifes中的模样:

<Extensions>
  <Extension Category="windows.backgroundTasks" EntryPoint="Tasks.ExampleBackgroundTask">
    <BackgroundTasks>
      <Task Type="systemEvent" />
    </BackgroundTasks>
  </Extension>
</Extensions>

将异进程幕后任务转为同进程的,最简单的做法是将IBackgroundTask.Run 中的代码迁移到OnBackgroundActivated中

多个幕后任务,可以通过BackgroundActivatedEventArgs.TaskInstance.Task.Name来区分。

同进程幕后任务与前台之间不需要额外的通信机制,因为在同一个STA中。

同进程幕后任务有一些限制:

  • 不能执行VoIP
  • 不支持DeviceUseTrigger, DeviceServicingTrigger 以及 IoTStartupTask。

(本篇完)

2020-11-22更新

You have to add the SystemConditionType.SessionConnected condition, this condition happen every time the user log on to Windows.

An app must be placed on the lock screen before it can successfully register background tasks using this trigger type.

Edit:

On this url you can find the official documentation about what you need, and how to use it:

https://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh977056.aspx

https://msdn.microsoft.com/en-us/library/windows/apps/xaml/windows.applicationmodel.background.systemtriggertype.aspx

(更新)

comments powered by Disqus