Werken met achtergrondtaken in Windows-apps

Opmerking

In dit artikel worden achtergrondtaken behandeld die zijn gebouwd met de Windows Runtime (WinRT) BackgroundTaskBuilder-API in de Windows. ApplicationModel.Background naamruimte voor apps met pakketidentiteit, waaronder UWP en verpakte bureaublad-apps. Als u een nieuwe app bouwt of een bestaande app migreert naar Windows App SDK, raadpleegt u Achtergrondtaken gebruiken in Windows apps en Strategie voor taakmigratie op de achtergrond.

Meer informatie over het maken en registreren van een achtergrondtaak in uw app met de klasse Windows Runtime (WinRT) BackgroundTaskBuilder.

Een achtergrondtaak registreren

Zie het voorbeeld BackgroundTask voor een volledig voorbeeld van het registreren van een achtergrondtaak in een Universal Windows Platform (UWP)-app.

In het volgende voorbeeld ziet u de registratie van een Win32 COM-taak die wordt uitgevoerd op een terugkerende timer van 15 minuten.

Als u een achtergrondtaak wilt registreren, moet u eerst een nieuw exemplaar van de klasse BackgroundTaskBuilder maken. De BackgroundTaskBuilder klasse wordt gebruikt om achtergrondtaken in uw app te maken en te registreren. In het volgende codevoorbeeld ziet u hoe u een nieuw exemplaar van de BackgroundTaskBuilder klasse maakt:

using System;
using Windows.ApplicationModel.Background;

public IBackgroundTaskRegistration RegisterBackgroundTaskWithSystem(IBackgroundTrigger trigger, Guid entryPointClsid, string taskName)
{
    BackgroundTaskBuilder builder = new BackgroundTaskBuilder();

    builder.SetTrigger(trigger);
    builder.SetTaskEntryPointClsid(entryPointClsid);

    BackgroundTaskRegistration registration;
    if (builder.Validate())
    {
        registration = builder.Register(taskName);
    }
    else
    {
        registration = null;
    }

    return registration;
}

RegisterBackgroundTaskWithSystem(new TimeTrigger(15, false), typeof(TimeTriggeredTask).GUID, typeof(TimeTriggeredTask).Name);

De RegisterBackgroundTaskWithSystem methode heeft drie parameters:

  • trigger: De trigger waarmee de achtergrondtaak wordt gestart.
  • entryPointClsid: De klasse-id van het beginpunt van de achtergrondtaak.
  • taskName: De naam van de achtergrondtaak.

Met RegisterBackgroundTaskWithSystem de methode maakt u een nieuw exemplaar van de BackgroundTaskBuilder klasse en stelt u de klasse-id van het trigger- en toegangspunt voor de achtergrondtaak in. De methode registreert vervolgens de achtergrondtaak bij het systeem.

Opmerking

Deze klasse is niet flexibel, wat betekent dat u rekening moet houden met het threadingmodel en het marshaling-gedrag. Zie Threading and Marshaling (C++/CX) and Using Windows Runtime objects in a multithreaded environment (.NET) voor meer informatie.

Moderne standby verwerken in een achtergrondtaak

Met de BackgroundTaskBuilder en gerelateerde API's kunnen verpakte bureaubladtoepassingen al achtergrondtaken uitvoeren. De API breidt deze API's nu uit om applicaties in staat te stellen code uit te voeren in moderne standby. De update voegt ook eigenschappen toe die kunnen worden opgevraagd door een app om te bepalen of het systeem achtergrondtaken voor de toepassing in moderne stand-by beperkt om de levensduur van de batterij te besparen. Dit maakt scenario's mogelijk, zoals apps die VoIP-aanroepen of andere pushmeldingen ontvangen van moderne stand-by.

Opmerking

'Verpakte bureaubladtoepassingen' in deze sectie verwijst naar Win32-toepassingen die een pakketidentiteit hebben (d.w.z. Desktop Bridge of Sparse Signed Packaged applications) en hebben een hoofdfunctie (of wmain) als toegangspunt.

In het volgende voorbeeld ziet u hoe een app-ontwikkelaar de BackgroundTaskBuilder-API kan gebruiken om maximaal één taak te registreren met de opgegeven taaknaam. In het voorbeeld ziet u ook hoe u de taakregistratie kunt controleren en instellen om uitgevoerd te worden in moderne stand-by voor de meest kritieke taken van de toepassing.

// The following namespace is required for BackgroundTaskBuilder APIs. 
using Windows.ApplicationModel.Background; 

// The following namespace is required for API version checks. 
using Windows.Foundation.Metadata; 

// The following namespace is used for showing Toast Notifications. This 
// namespace requires the Microsoft.Toolkit.Uwp.Notifications NuGet package 
// version 7.0 or greater. 
using Microsoft.Toolkit.Uwp.Notifications; 

// Incoming calls are considered to be critical tasks to the operation of the app. 
const string IncomingCallTaskName = "IncomingCallTask"; 
const string NotificationTaskName = "NotificationTask"; 
const string PrefetchTaskName = "PrefetchTask"; 

public static bool IsAllowedInBackground(BackgroundAccessStatus status) { 
    return ((status != BackgroundAccessStatus.Denied) && 
            (status != BackgroundAccessStatus.DeniedBySystemPolicy) && 
            (status != BackgroundAccessStatus.DeniedByUser) && 
            (status != BackgroundAccessStatus.Unspecified)); 
} 

public async void RegisterTask(IBackgroundTrigger trigger, 
                               Guid entryPointClsid, 
                               string taskName, 
                               bool isRunInStandbyRequested) 
{ 
    var taskBuilder = new BackgroundTaskBuilder(); 
    taskBuilder.SetTrigger(trigger); 
    taskBuilder.SetTaskEntryPointClsid(entryPointClsid); 

    // Only the most critical background work should be allowed to proceed in 
    // modern standby. Additionally, some platforms may not support modern 
    // or running background tasks in modern standby at all. Only attempt to 
    // request modern standby execution if both are true. Requesting network 
    // is necessary when running in modern standby to handle push notifications. 
    if (IsRunInStandbyRequested && taskBuilder.IsRunningTaskInStandbySupported) 
    { 
        var accessStatus = BackgroundExecutionManager.GetAccessStatusForModernStandby(); 
        if (!IsAllowedInBackground(accessStatus) 
        { 
            await BackgroundExecutionManager.RequestAccessKindForModernStandby( 
                    BackgroundAccessRequestKind.AllowedSubjectToSystemPolicy, 
                    "This app wants to receive incoming notifications while your device is asleep"); 
        } 

        accessStatus = BackgroundExecutionManager.GetAccessStatusForModernStandby(); 

        if (IsAllowedInBackground(accessStatus) 
        { 
            taskBuilder.IsRunningTaskInStandbyRequested = true; 
            taskBuilder.IsNetworkRequested = true; 
        } 
    } 

    // Check that the registration is valid before attempting to register. 
    if (taskBuilder.IsRegistrationValid) 
    { 
        // If a task with the specified name already exists, it is unregistered 
        // before a new one is registered. Note this API may still fail from 
        // catastrophic failure (e.g., memory allocation failure). 
        taskBuilder.Register(taskName); 
    } 

    return; 
} 

RegisterTask(new PushNotificationTrigger(), "{INSERT-YOUR-GUID-HERE}", IncomingCallTaskName, true); 

Controleren of achtergrondtaken hun budget in Modern Standby hebben overschreden

De volgende voorbeeldcode laat zien hoe een app-ontwikkelaar de BackgroundWorkCost.WasApplicationThrottledInStandby en BackgroundWorkCost.ApplicationEnergyUseLevel kan gebruiken om te monitoren en te reageren op hun achtergrondtaken die het app-budget uitputten. De app-ontwikkelaar kan reageren door het verminderen van werk met een lagere prioriteit dat wordt uitgevoerd in de moderne stand-by. Houd er rekening mee dat dit afhankelijk is van de code uit het vorige voorbeeld.

public async void ReduceBackgroundCost() 
{ 
    BackgroundTaskRegistration callTask; 
    BackgroundTaskRegistration notificationTask; 
    BackgroundTaskRegistration prefetchTask; 

    // Nothing to do if the app was not or will not be throttled. 
    if (!BackgroundWorkCost.WasApplicationThrottledInStandby && 
        (BackgroundWorkCost.ApplicationEnergyUseLevel != StandbyEnergyUseLevel.OverBudget)) 
    { 
        return; 
    } 

    foreach (var task in BackgroundTaskRegistration.AllTasks) 
    { 
        switch (task.Value.Name) { 
        case IncomingCallTaskName: 
            callTask = task.Value; 
            break; 

        case NotificationTaskName: 
            notificationTask = task.Value; 
            break; 

        case PrefetchTaskName: 
            prefetchTask = task.Value; 
            break; 

        default: 
        } 
    } 

    if (callTask.WasTaskThrottledInStandby) 
    { 
        // Unset the throttle flag after acknowledging it so the app can 
        // react to the same task being throttled again in the future. 
        task.Value.WasTaskThrottledInStandby = false; 

        // Notify the user that the notification was missed. 
        new ToastContentBuilder() 
            .AddText("You missed a call") 
            .AddText(task.Value.Name) 
            .Show(); 

        // Because the incoming calls were not activated, demote less notifications 
        // tasks so the calls can be delivered promptly in the future. 
        RegisterTask(notificationTask.Value.Trigger, 
                     typeof(TimeTriggeredTask).GUID, 
                     notificationTask.Value.Name, 
                     false); 
    } 

    // Note that if incoming call tasks were throttled in some previous modern 
    // standby session, the application energy use was over budget for some period. 
    // Demote unimportant tasks like prefetch work to avoid calls and notifications 
    // from being throttled.
    if (callTask.WasTaskThrottledInStandby) ||
        (BackgroundWorkCost.ApplicationEnergyUseLevel == StandbyEnergyUseLevel.OverBudget))
    {
        RegisterTask(prefetchTask.Value.Trigger,
                     typeof(TimeTriggeredTask).GUID,
                     prefetchTask.Value.Name,
                     false);
    }

    return;
}

Hier volgt een incrementele end-to-end-update voor de volgende C++WinRT/C#-voorbeeldcode op GitHub.

In het voorbeeld ziet u hoe u de BackgroundWorkCost.ApplicationEnergyUseTrend kunt gebruiken om te controleren hoe uw achtergrondtaken trenden in de richting van uitputting van hun budget. U kunt ook voorkomen dat de duurste achtergrondtaken in moderne stand-by worden uitgevoerd en voorkomen dat achtergrondtaken in moderne stand-by worden uitgevoerd als hun app het budget te snel gebruikt. Dit voorbeeld is afhankelijk van code uit eerdere voorbeelden.

public async void ReduceBackgroundCostPreemptively() 
{ 
    BackgroundTaskRegistration mostExpensiveTask = null; 

    // We can't do anything preemptively since the trend isn't known. 
    if (!BackgroundWorkCost.IsApplicationEnergyUseTrendKnown) 
    { 
        return; 
    } 

    // The app is not trending towards being over budget, so this method can 
    // return early. 
    if ((BackgroundWorkCost.ApplicationEnergyUseTrend != EnergyUseTrend.OverBudget) && 
        (BackgroundWorkCost.ApplicationEnergyUseTrend != EnergyUseTrend.OverHalf)) 
    { 
        return; 
    } 

    // The application is going exceeding its budget very quickly. Demote the 
    // most expensive task that is not the call task before call tasks start being 
    // throttled. 
    if (BackgroundWorkCost.ApplicationEnergyUseTrend == EnergyUseTrend.OverBudget) 
    { 
        foreach (var task in BackgroundTaskRegistration.AllTasks) 
        { 
            if ((task.Value.Name != IncomingCallTaskName) && 
                ((mostExpensiveTask == null) || 
                 (mostExpensiveTask.ApplicationEnergyUseTrendContributionPercentage < 
                  task.Value.ApplicationEnergyUseTrendContributionPercentage))) 
            { 
                mostExpensiveTask = task.Value; 
            } 
        } 
    } 

    if (mostExpensiveTask != null) 
    { 
        RegisterTask(mostExpensiveTask.Trigger, 
                     typeof(TimeTriggeredTask).GUID, 
                     mostExpensiveTask.Name, 
                     false); 
    } 

    // The application is trending toward eventually exceeding its budget. Demote the 
    // least important prefetch task before calls and notifications are throttled. 
    foreach (var task in BackgroundTaskRegistration.AllTasks) 
    { 
        if (task.Value.Name == PrefetchTaskName) { 
            RegisterTask(task.Value.Trigger, 
                         typeof(TimeTriggeredTask).GUID, 
                         task.Value.Name, 
                         false); 
        } 
    } 

    return; 
} 

Achtergrondtaken en netwerkconnectiviteit

Als voor uw achtergrondtaak netwerkconnectiviteit is vereist, moet u rekening houden met de volgende overwegingen.

  • Gebruik een SocketActivityTrigger om de achtergrondtaak te activeren wanneer een pakket wordt ontvangen en u moet een kortstondige taak uitvoeren. Nadat de taak is uitgevoerd, moet de achtergrondtaak worden beëindigd om energie te besparen.
  • Gebruik een ControlChannelTrigger om de achtergrondtaak te activeren wanneer een pakket wordt ontvangen en u moet een langdurige taak uitvoeren.
  • Voeg de voorwaarde InternetAvailable (BackgroundTaskBuilder.AddCondition) toe aan uw achtergrondtaak om de achtergrondtaak uit te stellen totdat de netwerkstack wordt uitgevoerd. Deze voorwaarde bespaart energie omdat de achtergrondtaak pas wordt uitgevoerd als er netwerktoegang beschikbaar is. Deze voorwaarde biedt geen realtime activering.
  • Ongeacht de trigger die u gebruikt, stelt u IsNetworkRequested in op uw achtergrondtaak om ervoor te zorgen dat het netwerk actief blijft terwijl de achtergrondtaak wordt uitgevoerd. Dit vertelt de infrastructuur van de achtergrondtaak om het netwerk actief te houden terwijl de taak wordt uitgevoerd, zelfs als het apparaat de Connected Standby-modus heeft opgegeven. Als uw achtergrondtaak IsNetworkRequested niet gebruikt, heeft uw achtergrondtaak geen toegang tot het netwerk in de modus Verbonden stand-by.