天天看點

UWP -- Background Task 深入解析

1. 重點

鎖屏問題

從 Windows 10 開始,使用者無須再将你的應用添加到鎖屏界面,即可利用背景任務,通用 Windows 應用必須在注冊任何背景觸發器類型之前調用 RequestAccessAsync:

await      

資源限制

由于對于記憶體較低的裝置的資源限制,背景任務可能具有記憶體限制,該限制決定了背景任務可以使用的記憶體上限

在記憶體受限的裝置上,對于可以安裝在一台裝置上并在任何給定時間使用背景任務的應用數量有所限制。 如果超出該數量,将無法調用注冊所有背景任務時所需的 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

  1. Implement the code you want executed as a background task.
  2. Decide what triggers your background task code (eg: timer , user log in , ..)
  3. Add a manifest declaration so that Windows knows how to active background task code.

    隻是決定什麼情況下觸發,進而運作

  4. 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. 背景任務指南

内外程序中的任務

UWP -- Background Task 深入解析

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();
        //      

準備應用更新

如果你的應用将更新,請建立和注冊一個 ServicingComplete 背景任務,以便取消注冊适用于前一版本應用的背景任務,并注冊适用于新版本應用的背景任務。 此時也非常适合在前台運作的上下文之外執行所需的應用更新

建議每個 app 都可以裝一個,它可以在 你沒運作之前做一些操作

5. 1.Implement your Background Task’s code

// 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      

使用事件處理程式處理背景任務完成

你應該使用 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.

8.