Idioma

Trabalhando com tarefas em segundo plano em aplicativos Windows

Observação

Este artigo aborda tarefas em segundo plano criadas com a API Windows Runtime BackgroundTaskBuilder no Windows.ApplicationModel.Background namespace para apps com identidade de pacote, incluindo UWP e apps de desktop empacotados. Se você estiver criando um novo aplicativo ou migrando um aplicativo existente para o SDK do Aplicativo Windows, consulte Usando tarefas em segundo plano em aplicativos Windows e Estratégia de migração de tarefas em segundo plano.

Saiba como criar e registrar uma tarefa em segundo plano em seu aplicativo com a classe Windows Runtime (WinRT) BackgroundTaskBuilder.

Registrar uma tarefa em segundo plano

Consulte o exemplo BackgroundTask para obter um exemplo completo de registro de uma tarefa em segundo plano em um aplicativo Plataforma Universal do Windows (UWP).

O exemplo a seguir mostra o registro de uma tarefa COM do Win32 que é executada em um temporizador recorrente de 15 minutos.

Para registrar uma tarefa em segundo plano, primeiro você deve criar uma nova instância da classe BackgroundTaskBuilder . A BackgroundTaskBuilder classe é usada para criar e registrar tarefas em segundo plano em seu aplicativo. O exemplo de código a seguir demonstra como criar uma nova instância da BackgroundTaskBuilder classe:

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

O RegisterBackgroundTaskWithSystem método usa três parâmetros:

  • trigger: o gatilho que iniciará a tarefa em segundo plano.
  • entryPointClsid: a ID da classe do ponto de entrada da tarefa em segundo plano.
  • taskName: O nome da tarefa em segundo plano.

O método RegisterBackgroundTaskWithSystem cria uma nova instância da classe BackgroundTaskBuilder e define o ID de classe de gatilho e ponto de entrada para a tarefa em segundo plano. Em seguida, o método registra a tarefa em segundo plano com o sistema.

Observação

Esta classe não é ágil, o que significa que você precisa considerar o modelo de threading e o comportamento de marshaling. Para obter mais informações, consulte Threading and Marshaling (C++/CX) e Usando objetos Windows Runtime em um ambiente multithread (.NET).

Processar em espera moderno em uma tarefa em segundo plano

O BackgroundTaskBuilder e as APIs relacionadas já permitem que aplicativos de área de trabalho empacotados executem tarefas em segundo plano. A API agora estende essas APIs para permitir que esses aplicativos executem código em espera moderno. A atualização também adiciona propriedades que podem ser consultadas por um aplicativo para determinar se o sistema restringirá as tarefas em segundo plano do aplicativo em modo de espera moderno para conservar a vida útil da bateria. Isso permite cenários como aplicativos que recebem chamadas VoIP ou outras notificações push do modo de espera moderno.

Observação

"Aplicativos desktop empacotados" nesta seção se refere a aplicativos Win32 que tenham identidade do pacote (ou seja, são aplicativos de ponte desktop ou assinados esparsos) e têm uma função principal (ou wmain) como o ponto de entrada.

O exemplo a seguir mostra como um desenvolvedor de aplicativos pode usar a API BackgroundTaskBuilder para registrar no máximo uma tarefa com o nome da tarefa especificado. O exemplo também mostra como verificar e optar pelo registro de tarefas para serem executadas em modo de espera moderno nas tarefas mais críticas do aplicativo.

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

Verificar se as tarefas em segundo plano excederam seu orçamento no Modern Standby

O código de exemplo a seguir mostra como um desenvolvedor de aplicativos pode usar o BackgroundWorkCost.WasApplicationThrottledInStandby e BackgroundWorkCost.ApplicationEnergyUseLevel para monitorar e reagir a fazer com que suas tarefas em segundo plano esgote o orçamento do aplicativo. O desenvolvedor do aplicativo pode reagir reduzindo o trabalho de menor prioridade realizado durante a espera moderna. Observe que isso depende do código do exemplo anterior.

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;
}

Veja a seguir uma atualização incremental de ponta a ponta para o seguinte código de exemplo C++WinRT/C# em GitHub.

O exemplo mostra como você pode usar o BackgroundWorkCost.ApplicationEnergyUseTrend para monitorar como suas tarefas em segundo plano têm a tendência de esgotar seu orçamento. Você também pode impedir que as tarefas em segundo plano mais caras sejam executadas em espera moderna e evitar que outras tarefas sejam executadas assim, caso o aplicativo esteja usando o orçamento rapidamente. Este exemplo depende do código de exemplos anteriores.

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; 
} 

Tarefas em segundo plano e conectividade de rede

Se sua tarefa em segundo plano exigir conectividade de rede, esteja ciente das considerações a seguir.

  • Use um SocketActivityTrigger para ativar a tarefa em segundo plano quando um pacote é recebido e você precisa executar uma tarefa de curta duração. Depois de executar a tarefa, a tarefa em segundo plano deve ser encerrada para economizar energia.
  • Use um ControlChannelTrigger para ativar a tarefa em segundo plano quando um pacote é recebido e você precisa executar uma tarefa de longa duração.
  • Adicione a condição InternetAvailable (BackgroundTaskBuilder.AddCondition) à tarefa em segundo plano BackgroundTaskBuilder.AddCondition para atrasar o disparo da tarefa em segundo plano até que a pilha de rede esteja em execução. Essa condição economiza energia porque a tarefa em segundo plano não será executada até que o acesso à rede esteja disponível. Essa condição não fornece ativação em tempo real.
  • Independentemente do gatilho usado, defina IsNetworkRequested em sua tarefa em segundo plano para garantir que a rede permaneça ativa enquanto a tarefa em segundo plano é executada. Isso informa a infraestrutura de tarefas em segundo plano para manter a rede ativa enquanto a tarefa está em execução, mesmo que o dispositivo tenha entrado no modo Connected Standby. Se sua tarefa em segundo plano não usar IsNetworkRequested, sua tarefa em segundo plano não poderá acessar a rede quando estiver no modo De espera conectado.