今天在爆UWP的定时后台任务,坑有点多。有的坑在很多网上的文章里都没提到,非常的坑。刚刚开荒成功了,把经验写出来分享:
1. 写一个后台任务的类,继承IBackgroundTask接口
通常,在设计应用程序结构的时候,我们会建类库项目(Class Library)放这些类。比如 FarkBackgroundTask.Core
因为是UWP工程,所以建的类库也要是Universal Windows的。注意,这里我们已经埋下了一个巨坑,稍后会解释。
我们的类代码如下:
public class SayFarkTask : IBackgroundTask
{
public void Run(IBackgroundTaskInstance taskInstance)
{
Debug.Write("================ Fark the farking farkers ================");
}
}
里面这个Run方法,就是执行任务用的,是接口里的方法。现在,我们又埋下了一个巨坑。
2. 在manifest文件里添加后台任务声明
对应的代码是:
<Extensions>
<Extension Category="windows.backgroundTasks" EntryPoint="FarkBackgroundTask.Core.SayFarkTask">
<BackgroundTasks>
<Task Type="timer" />
</BackgroundTasks>
</Extension>
</Extensions>
3. 在应用程序启动的时候注册后台任务
踩过一堆小坑之后,封装了一个注册方法:
public static async Task<BackgroundTaskRegistration> RegisterBackgroundTask(Type taskEntryPoint,
string taskName,
IBackgroundTrigger trigger,
IBackgroundCondition condition)
{
var status = await BackgroundExecutionManager.RequestAccessAsync();
if (status == BackgroundAccessStatus.Unspecified || status == BackgroundAccessStatus.Denied)
{
return null;
}
foreach (var cur in BackgroundTaskRegistration.AllTasks)
{
if (cur.Value.Name == taskName)
{
cur.Value.Unregister(true);
}
}
var builder = new BackgroundTaskBuilder
{
Name = taskName,
TaskEntryPoint = taskEntryPoint.FullName
};
builder.SetTrigger(trigger);
if (condition != null)
{
builder.AddCondition(condition);
}
BackgroundTaskRegistration task = builder.Register();
Debug.WriteLine($"Task {taskName} registered successfully.");
return task;
}
主要有几点小坑:
- 先得请求权限,timer的任务是需要权限的
- 如果注册过,建议先清除注册,再重新注册一次。这里我的代码和MSDN上的例子不太一样了,MSDN的做法是foreach里检查到name一样的就return那个task,但是我考虑的是如果你的task注册行为有变化就不好管理了,所以干脆全删了重建一个新的。
- 入口点EntryPoint的类型我改用了Type,好处是强类型,编译时就知道会不会爆,防止手滑。MSDN的例子是用string的,容易改爆。
现在就可以在App.xaml.cs里面注册了:
private async Task RegisterBackgroundTask()
{
var task = await RegisterBackgroundTask(
typeof(FarkBackgroundTask.Core.SayFarkTask),
"SayFarkTask",
new TimeTrigger(15, false),
null);
task.Progress += TaskOnProgress;
task.Completed += TaskOnCompleted;
}
在protected override async void OnLaunched(LaunchActivatedEventArgs e)里面:
...
Window.Current.Content = rootFrame;
await RegisterBackgroundTask();
...
现在看上去一切都是好的,然而运行就会爆出屎来,你会发现Task怎么也激活不了。这是因为之前埋下了2个巨坑。
4. 巨坑之一 sealed 类
因为一个神奇的原因,我们的后台任务类SayFarkTask如果不是sealed,就会爆。所以得改成:
public sealed class SayFarkTask : IBackgroundTask
...
现在运行还是会爆,爆了我2个多小时候,我发现了个巨坑!简直坑得史无前例……
5. 巨坑之二 Windows Runtime Component
在我们建类库的时候,选的Universal Windows没错,但是它的输出类型是Class Library,我们平时写应用确实一直在用Class Library,但是这里不行,必须改成Windows Runtime Component,不然就爆!!!
坑就坑在MSDN下载的范例里面一下子也发现不了,毕竟在VS里显示的时候都叫(Universal Windows)……
6. 现在执行应用程序,在VS的lifecycle events里选我们的SayFarkTask就能跑出结果了
代码见github:https://github.com/EdiWang/UWP-Demos/tree/master/FarkBackgroundTask