frendguo's blog

UWP开发之后台任务

官方文档看这里: https://docs.microsoft.com/zh-cn/windows/uwp/launch-resume/support-your-app-with-background-tasks

关于后台任务

我们都知道, 对于UWP应用, 当应用未处于前台时, 都会被OS挂起. 此时, 该应用的所有代码都将不再执行. 然而, 有些程序需要在它处于后台挂起状态(或者终止状态)的时候, 还能运行某些代码. 对于这些应用, WinRT提供了后台任务的机制, 方便应用不在前台时还能执行某些代码.

上图只是一个简单的App, Windows以及后台任务代码之间的交互过程, 具体的过程会在后面的例子中有所体现.

后台任务应用分类

  • 后台播放媒体 
  • 后台文件传输
  • 后台更新磁贴(Tile), 吐司通知(Toast)和锁屏提醒(Lock Screen)
  • 其他后台任务

后台任务的资源限制

  • 时间限制: 后台任务一般限定在30秒内执行, 最多可运行10分钟
  • 内存限制: 无确定的值, 根据设备内存大小而定. 可通过 MemoryManager 类进行查询. 如果设备内存不足, 后台任务可能会终止
  • 其他约束: 包括但不限于 节电模式, 用户禁止后台任务 ......

注: 以下将以"每隔30分钟发送一封Email"为例, 实现进程外的后台任务. 关于进程内的后台任务, 可以戳这里>>

Step 1: 实现后台任务代码

此部分的文档: https://docs.microsoft.com/zh-cn/windows/uwp/launch-resume/create-and-register-a-background-task

先上代码, 再来说说那些坑

using Windows.ApplicationModel.Background;

namespace SendEmailBackgroundTask
{
    public sealed class BackgroundTask : IBackgroundTask
    {
        BackgroundTaskDeferral _deferral;
 
        public async void Run(IBackgroundTaskInstance taskInstance)
        {
            int count = 6;
            _deferral = taskInstance.GetDeferral();

            if (!LoadInfo())
            {
                _deferral.Complete();
                return;
            }

            while (!(await SendIPToEmail()) && count > 0)
            {
                count--;
            }

            _deferral.Complete();
        }
    }
}
  1. 向解决方案添加项目的时候必须是 "Windows运行时组件(通用Windows)
  2. 后台任务类本身和后台任务项目中的所有其他类都需要是处于 sealed 状态的 public 类。
  3. BackgroundTaskDeferral _deferral; 如果Run方法中用到了异步方法, 则需要使用延时. 并且得声明在异步方法调用之前.
  4. 该类是继承自 IBackgroundTask, 即必须得实现Run方法.  并且是封闭类
  5. Run() 方法内部执行我们需要得任何代码. 每次Run方法被调用的时候, 都会传递一个新的 IBackgroundTask 对象. 

Step 2: 确定后台任务代码的触发器

确定触发事件, 以此启动后台任务代码的载入和执行.下表列出了应用程序使用的主要触发器类型

维护触发器和计时触发器

维护触发器: https://docs.microsoft.com/zh-cn/windows/uwp/launch-resume/use-a-maintenance-trigger

计时触发器: https://docs.microsoft.com/zh-cn/windows/uwp/launch-resume/run-a-background-task-on-a-timer-

相同点:

  • 都会在未来某个时刻执行代码
  • 每天至多被触发96次(相当于每15分钟触发一次), 如果低于15分钟, 在任务注册的时候将会引发异常
  • 可以通过配置OneShot 来确定是执行一次还是始终执行

区别:

  • 维护触发器仅在PC接通AC(交流)电源时才能执行代码.而计时器触发器可在PC接通AC电源或者使用电池的时候触发.因此, 计时器触发器跟便于使用.
  • 维护触发器常用于不太紧急的任务, 而计时器触发器则用于紧急执行的任务

创建一个30分钟的计时器触发器, 并且设定始终执行

TimeTrigger hourlyTrigger = new TimeTrigger(30, false);

创建一个30分钟的维护触发器, 并且设定始终执行

MaintenanceTrigger hourlyTrigger = new MaintenanceTrigger(30, false);

系统触发器

参考: https://docs.microsoft.com/zh-cn/windows/uwp/launch-resume/respond-to-system-events-with-background-tasks

系统触发器允许在某个与系统相关事件发生的时候被运行.

Step 3: 添加清单声明

一旦实现了后台任务的代码, 并且确定了需要使用的触发器类型, 就可以将后台任务的组件告知Windows了.为此, 我们需要在应用程序的程序包清单添加声明.

具体操作参考官方文档: https://docs.microsoft.com/zh-cn/windows/uwp/launch-resume/declare-background-tasks-in-the-application-manifest

注: 程序入口点是: (命名空间名).(类名)

Step 4: 注册应用程序的后台任务

一旦我们在.winmd文件中实现了WinRT 后台任务组件, 并在清单中声明了应用程序的后台任务, 应用程序就可以在Windows注册后台任务了.

注册进程内后台任务: https://docs.microsoft.com/zh-cn/windows/uwp/launch-resume/create-and-register-an-inproc-background-task

注册进程外后台任务: https://docs.microsoft.com/zh-cn/windows/uwp/launch-resume/create-and-register-a-background-task

这里, 还是上代码(UWP 主程序中 封装的注册函数)

public static async Task<BackgroundTaskRegistration> RegisterBackgroundTaskAsync(
                                                            Type taskEntryPoint,
                                                            string name,
                                                            IBackgroundTrigger trigger,
                                                            IBackgroundCondition condition)
{
    // 先得请求权限 
    var status = await BackgroundExecutionManager.RequestAccessAsync();
    if (status == BackgroundAccessStatus.Unspecified || status == BackgroundAccessStatus.DeniedBySystemPolicy
        || status == BackgroundAccessStatus.DeniedByUser)
    {
        Debug.WriteLine($"Task {name} registered fail.");
        return null;
    }

    // 检查任务是否已经注册
    foreach (var registerTask in BackgroundTaskRegistration.AllTasks)
    {
        if (registerTask.Value.Name == name)
        {
            // 已注册 则直接返回 -- msdn
            // return (BackgroundTaskRegistration)registerTask.Value;

            // 已注册 先取消注册 再重新注册 方便更改
            registerTask.Value.Unregister(true);
        }
    }

    // 注册后台任务
    var builder = new BackgroundTaskBuilder();

    if (taskEntryPoint != null && taskEntryPoint.FullName != string.Empty)
    {
        builder.TaskEntryPoint = taskEntryPoint.FullName;
    }
    builder.Name = name;
    builder.SetTrigger(trigger);
    if (condition != null)
    {
        builder.AddCondition(condition);
    }
    builder.IsNetworkRequested = true;

    BackgroundTaskRegistration task = builder.Register();

    Debug.WriteLine($"Task {name} registered successed!");
    return task;
}

调用注册方法注册后台任务(满足一定条件则注册, UWP主程序中)

// 注册后台任务
await BackgroundTaskConfig.RegisterBackgroundTaskAsync(typeof(SendEmailBackgroundTask.BackgroundTask),
                                        "BackgroundTaskName",
                                        new TimeTrigger(30, false),
                                        new SystemCondition(SystemConditionType.InternetAvailable)
                                        );

Step 5: 调试后台任务(可选)

参考官方文档: https://docs.microsoft.com/zh-cn/windows/uwp/launch-resume/debug-a-background-task

Step 6: 后台任务的进度和完成(可选)

参考官方文档: https://docs.microsoft.com/zh-cn/windows/uwp/launch-resume/monitor-background-task-progress-and-completion

Step 7: 处理取消的后台任务(可选)

参考官方文档: https://docs.microsoft.com/zh-cn/windows/uwp/launch-resume/handle-a-cancelled-background-task

到这里, UWP的后台任务大致结束了, 其他的应用都和这个类似.

Happy Coding!o(* ̄▽ ̄*)o

Add comment

Loading