原文:UWP -- Background Task 深入解析
1. 重点
锁屏问题
从 Windows 10 开始,用户无须再将你的应用添加到锁屏界面,即可利用后台任务,通用 Windows 应用必须在注册任何后台触发器类型之前调用 RequestAccessAsync:
await BackgroundExecutionManager.RequestAccessAsync();
资源限制
由于对于内存较低的设备的资源约束,后台任务可能具有内存限制,该限制决定了后台任务可以使用的内存上限
在内存受限的设备上,对于可以安装在一台设备上并在任何给定时间使用后台任务的应用数量有所限制。 如果超出该数量,将无法调用注册所有后台任务时所需的 RequestAccessAsync
如果后台任务尝试执行超过此限制的操作,则该操作将失败,并可能生成内存不足的异常情况,但该任务可以处理此情况。 如果该任务无法处理内存不足的异常情况,或者由于尝试的操作的性质导致无法生成内存不足的异常情况,任务将立即终止
你可以使用 MemoryManager PAI 查询当前内存使用量和限制,以发现上限(若有),并监视正在进行的后台任务内存使用量
进程内后台任务 entry-point
对于进程内后台活动,不要设置 TaskEntryPoint.。将其保留为空可启用默认入口点,这是 Application 对象上称为 OnBackgroundActivated() 的一种新的受保护方法
节电模式
除非你豁免你的应用,以便它可以在节电模式打开时仍可以运行后台任务和接收推送通知,否则当节电模式功能启用时,如果设备未连接到外部电源且电量低于指定剩余电量,它将阻止后台任务运行。 这不会阻止你注册后台任务
时间限制
后台任务受其基于触发器类型获取的时钟时间使用的限制。
大多数触发器限制为 30 秒的时钟时间使用,如果后台活动的运行时间超过执行时间限制,即使在应用的前台进程内运行,后台活动也会终止(进程内后台任务)。而另一些触发器在完成耗时任务时最多可以运行 10 分钟。 为了延长电池使用时间并为前台应用提供最佳用户体验,后台任务应该是轻量级任务
2. 概述
Reason: Windows Store apps have their threads suspended when not in the forground.
Benefit: This prevents background apps fom consuming battery power and prevents apps the user is not interacting with from affecting the responsiveness of the foreground app.
- System can update your tile or show a toast notification even when your app is suspended or terminated. —- Toast and Notification
- Your app could transfer large files even when in the background. — Networking
All using background task. Windows Store apps can execute code when not in the foreground by using background task.
Background Task architecture
- Implement the code you want executed as a background task.
- Decide what triggers your background task code (eg: timer , user log in , ..)
- Add a manifest declaration so that Windows knows how to active background task code.
只是决定什么情况下触发,从而运行
- Register your app’s background tasks with Windows the first time your app is activated.
注意:
第一次需要在代码中注册
此外,需要在项目中添加对 WinRT 的引用,reference
Background task 启动过程
When a trigger occurs, Windows creates a new process, loads your background task code into it, and calls an entry point. The background task process runs in the package’s app container. (background task typically execute in a different process than the app’s process)
注:Windows Store apps runs in a different security context than a desktop apps. The security context is called app container, and it restricts which resources a Windows Store app can access.
a new process —- 独立进程 (backgroundtaskhost.exe)
需要说明,一般background task 运行在哪个进程,由manifest – declarations – background task – Executable 决定,文档上:
For most of the triggers, you must leave this blank, Which tells Windows to use its own BackgroundTaskHost.exe process.(系统的BackgroundTaskHost.exe, 任务管理器只看见一个)
Background is not allowed to update the app’s user interface, but can update tiles and badges, or cause the display of a toast notification
重点:
The background task process runs in the package’s app container. This means that the manifest’s package capabilities and app declarations do apply to the background task process. The background task process can write to the package’s data setting and folders. (for an app and its background tasks to communicate with each other). One process can signal when data is ready by calling ApplicationData’s SingalDataChanged method.
Your app must run at least once in order to register a background task and its desired trigger with Windows.(第一次在代码中注册)
Once registered, your app can be running, suspended, or even terminated. When the trigger occurs, Windows starts your app’s background task.
//在注册前运行即可
await Windows.ApplicationModel.Background.BackgroundExecutionManager.RequestAccessAsync();
Background task is the only way to execute code when a PC is in Connected Standby mode.
3. 使用后台任务支持应用
3.1 在后台播放媒体
3.2 进程内后台任务和进程外后台任务
有两种方法可以实现后台任务:
- 进程内
- 应用及其后台进程在同一进程中运行;
- 进程外
- 应用及其后台进程分别在单独的进程中运行。 进程内后台支持在 Windows 10 版本 1607 中引入,目的是简化编写后台任务。 但仍可以编写进程外后台任务
进程外后台任务实现为操作系统在独立进程 (backgroundtaskhost.exe) 中运行的轻型类。 进程外后台任务编写来实现 IBackgroundTask 接口的类。 通过使用 BackgroundTaskBuilder 类注册后台任务。 注册后台任务时,类名称将用于指定入口点
4. 后台任务指南
内外进程中的任务
CPU 配额
后台任务受其基于触发器类型获取的时钟时间使用的限制。 大多数触发器限制为 30 秒的时钟时间使用,而另一些触发器在完成耗时任务时最多可以运行 10 分钟。 为了延长电池使用时间并为前台应用提供最佳用户体验,后台任务应该是轻量级任务。
使用 BackgroundTaskDeferral
如果后台任务类运行异步代码,则确保使用延迟。 否则,当使用 Run 方法(或针对进程内后台任务使用 OnBackgroundActivated 方法)时,你的后台任务可能会提前终止
BackgroundTaskDeferral _deferral; // Note: defined at class scope so we can mark it complete inside the OnCancel() callback if we choose to support cancellation
public async void Run(IBackgroundTaskInstance taskInstance)
{
_deferral = taskInstance.GetDeferral()
//
// TODO: Insert code to start one or more asynchronous methods using the
// await keyword, for example:
//
// await ExampleMethodAsync();
//
_deferral.Complete();
}
准备应用更新
如果你的应用将更新,请创建和注册一个 ServicingComplete 后台任务,以便取消注册适用于前一版本应用的后台任务,并注册适用于新版本应用的后台任务。 此时也非常适合在前台运行的上下文之外执行所需的应用更新
建议每个 app 都可以装一个,它可以在 你没运行之前做一些操作
5. 1.Implement your Background Task’s code
namespace MyApp.BackgroundTasks
{ // NOTE: determines filename "MyApp.BackgroundTasks.WinMD"
using Windows.ApplicationModel.Background; // For IBackgroundTask & IBackgroundTaskInstance
using Windows.Storage;
// NOTE: WinRT components MUST be public and sealed
public sealed class MyBackgroundTask : IBackgroundTask
{
public void Run(IBackgroundTaskInstance taskInstance)
{ // Register cancelation handler (see the "Background task cancellation" section)
// NOTE: Once canceled, a task has 5 seconds to complete or the process is killed
taskInstance.Canceled += (IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason) =>
{
// TODO: Tell task it should cancel itself as soon as possible...
};
// Recommended: Adjust task behavior based on CPU and network availability
// For example: A mail app could download mail for all folders when cost is
// low and only download mail for the Inbox folder when cost is high
switch (BackgroundWorkCost.CurrentBackgroundWorkCost)
{
case BackgroundWorkCostValue.Low: // The task can use CPU & network
case BackgroundWorkCostValue.Medium: // The task can use some CPU & network
case BackgroundWorkCostValue.High: // The task should avoid using CPU & network
// This example records the last trigger time in an application data setting
// so the app can read it later if it chooses. We do regardless of work cost.
ApplicationData.Current.LocalSettings.Values["LastTriggerTime"] = DateTimeOffset.Now;
break;
}
}
}
}
Must implemented as a WinRT comment in VS. This project simply creates a dynamic-link library file. The file extension is .WinMD instead of .DLL .
When you register the full name of this class. The windows will be ready to execute this task ,it will try to load a .WinMD file whose name matches the namespace.
加载这文件时候,会先加载构造函数,不写系统默认一个空构造,你写了就不去加载这个空的构造函数。(包含run)
6. Decide what triggers your Background Task’s code
注册要运行的后台任务
通过在 BackgroundTaskRegistration.AllTasks
属性中迭代,查明后台任务是否已注册。 此步骤非常重要;如果应用不检查现有后台任务注册,则它可能会轻松多次注册该任务,这会导致性能问题和工作结束前超出任务的最大可用 CPU 时间
var taskRegistered = false;
var exampleTaskName = "ExampleBackgroundTask";
foreach (var task in BackgroundTaskRegistration.AllTasks)
{
if (task.Value.Name == exampleTaskName)
{
taskRegistered = true;
break;
}
}
如果后台任务尚未注册,则使用 BackgroundTaskBuilder 创建你的后台任务的一个实例。 任务入口点应为命名空间为前缀的后台任务的名称。
后台任务触发器控制后台任务何时运行。 有关可能的触发器的列表,请参阅 SystemTrigger。
例如,此代码创建一个新后台任务并将其设置为在 TimeZoneChanged 触发器引发时运行:
var builder = new BackgroundTaskBuilder();
builder.Name = exampleTaskName;
builder.TaskEntryPoint = "RuntimeComponent1.ExampleBackgroundTask";
builder.SetTrigger(new SystemTrigger(SystemTriggerType.TimeZoneChange, false));
(可选)在触发器事件发生后,你可以添加条件控制任务何时运行。 例如,如果你不希望在用户存在前运行任务,请使用条件 UserPresent。 有关可能条件的列表,请参阅 SystemConditionType
builder.AddCondition(new SystemCondition(SystemConditionType.UserPresent));
通过在 BackgroundTaskBuilder 对象上调用 Register 方法来注册后台任务。 存储 BackgroundTaskRegistration 结果,以便可以在下一步中使用该结果
BackgroundTaskRegistration task = builder.Register();
通用 Windows 应用必须在注册任何后台触发器类型之前调用 RequestAccessAsync:
await BackgroundExecutionManager.RequestAccessAsync();
使用事件处理程序处理后台任务完成
你应该使用 BackgroundTaskCompletedEventHandler 注册一个方法,以便应用可以从后台任务中获取结果。 当启动或恢复应用时,如果自从上次在应用前台运行后后台任务就已完成,将调用标记方法。 (如果应用当前位于前台时后台任务完成,将立即调用 OnCompleted 方法)
private void OnCompleted(IBackgroundTaskRegistration task, BackgroundTaskCompletedEventArgs args)
{
var settings = Windows.Storage.ApplicationData.Current.LocalSettings;
var key = task.TaskId.ToString();
var message = settings.Values[key].ToString();
UpdateUI(message);
}
回到已注册后台任务的位置。 在该代码行之后,添加一个新的 BackgroundTaskCompletedEventHandler 对象。 提供 OnCompleted 方法作为 BackgroundTaskCompletedEventHandler 构造函数的参数:
task.Completed += new BackgroundTaskCompletedEventHandler(OnCompleted);
在应用清单中声明你的应用使用后台任务
略^^
具体 Trigger 及其中区别
7. Add manifest declaration
The meaning of Executable field, this tells Windows which process to execute when the trigger fires. (!!!)
This process will load your WinRT component’s .WinMD file and execute your task’s code. There are two options for this field. For most of the triggers, you must leave this field blank, which tells Windows to use its own Background TaskHost.exe process. (Windows or App container?)
注释:Windows Store apps run in a different security context than desktop apps. This security context is called an app container (discussed in the appendix, “App containers”), and it restricts which resources a Windows Store app can access.
For a PushNotificationTrigger, you can leave this field blank or you can specify(指定) the name of your own app’s executable. If you use the latter, Windows will have your app’s process load the WinRT component and run the task in the same process as your app.
This is not the recommended thing to do, but it allows your background task’s code the ability to access the same state (memory) as your app. However, if your app is suspended, all threads but the thread running the background task code remain suspended, so you must not perform any interthread communication or deadlocks will occur. In addition, because the GUI thread remains suspended, the background task cannot update the app’s user interface. If the app’s process is not running, Windows will activate it, but the app is not launched with a main view or hosted view activation. The result of all this is that your background task cannot have any expectations of the app’s state and, in fact, the app might not have its state fully initialized.
For a ControlChannelTrigger, you must not leave the Executable field blank; instead, you must specify your app’s executable name and your WinRT component’s .WinMD file must load in the app’s process.
As mentioned previously, the ControlChannelTrigger is used for RTC apps, and these apps typically have a socket open in the background task. For the app to respond to the incoming call on the socket, the background task and the app have to share the same process. Everything I said earlier still holds true in this scenario too; that is, the app will not be fully initialized and you should avoid interthread communication.
For the declaration’s Entry Point field, enter the full name (including the namespace) of the WinRT class you created in step 1 (for example, MyApp.BackgroundTasks.MyBackgroundTask). This tells the host process the name of the .WinMD file to load (MyApp.BackgroundTasks.WinMD) and the name of the class to construct in order to call its Run method.
Lock-Screen allowing that app’s background tasks to consume system resources even when the PC is on battery power. These apps typically have a real-time networking requirement like a chat or VoIP application
最多显示七个,可以自己去系统里面设置(你的app得支持lock-screen 才行)