Crear y registrar una tarea en segundo plano fuera del proceso

API importantes

Crea una tarea en segundo plano y regístrala para ejecutarla cuando tu aplicación no esté en primer plano. Este tema muestra cómo crear y registrar una tarea en segundo plano que se ejecuta en un proceso independiente al proceso de aplicación. Para efectuar un trabajo en segundo plano directamente en la aplicación en primer plano, consulta Crear y registrar una tarea en segundo plano dentro del proceso.

Nota:

Si usas una tarea en segundo plano para reproducir contenido multimedia en segundo plano, consulta Reproducir elementos multimedia en segundo plano para obtener información sobre las mejoras en Windows 10, versión 1607, que lo hacen mucho más fácil.

Nota

Si va a implementar una tarea en segundo plano fuera de proceso en una aplicación de escritorio de C# con .NET 6 o posterior, use la compatibilidad de creación de C#/WinRT para crear un componente de Windows Runtime. Esto se aplica a las aplicaciones que usan las SDK de Aplicaciones para Windows, WinUI 3, WPF o WinForms. Consulte el ejemplo de tarea en segundo plano para obtener un ejemplo.

Creación de la clase de tareas en segundo plano

Puedes ejecutar código en segundo plano escribiendo clases que implementen la interfaz IBackgroundTask. Este código se ejecuta cuando se desencadena un evento específico mediante, por ejemplo, SystemTrigger o MaintenanceTrigger.

En estos pasos te mostramos cómo escribir una nueva clase que implemente la interfaz IBackgroundTask.

  1. Crea un nuevo proyecto para tareas en segundo plano y agrégalo a tu solución. Para ello, haga clic con el botón derecho en el nodo de la solución en el Explorador de soluciones y seleccione Agregar>nuevo proyecto. A continuación, seleccione el Windows Runtime tipo de proyecto Componente, asigne un nombre al proyecto y haga clic en Aceptar.
  2. Haz referencia al proyecto de tareas en segundo plano desde tu proyecto de aplicación de la Plataforma universal de Windows (UWP). Para una aplicación de C# o C++, en el proyecto de la aplicación, haga clic con el botón derecho en Referencias y seleccione Agregar nueva referencia. En Solución, selecciona Proyectos, selecciona el nombre de tu proyecto de tarea en segundo plano y haz clic en Aceptar.
  3. En el proyecto de tareas en segundo plano, agregue una nueva clase que implemente la interfaz IBackgroundTask . El método IBackgroundTask.Run es un punto de entrada necesario al que se llamará cuando se desencadene el evento especificado; este método es necesario en todas las tareas en segundo plano.

Nota:

La propia clase de tarea en segundo plano (y todas las demás clases del proyecto de tarea en segundo plano) deben ser clases públicasselladas (o finales).

El código de ejemplo siguiente muestra un punto de partida muy básico para una clase de tarea en segundo plano.

// ExampleBackgroundTask.cs
using Windows.ApplicationModel.Background;

namespace Tasks
{
    public sealed class ExampleBackgroundTask : IBackgroundTask
    {
        public void Run(IBackgroundTaskInstance taskInstance)
        {
            
        }        
    }
}
// First, add ExampleBackgroundTask.idl, and then build.
// ExampleBackgroundTask.idl
namespace Tasks
{
    [default_interface]
    runtimeclass ExampleBackgroundTask : Windows.ApplicationModel.Background.IBackgroundTask
    {
        ExampleBackgroundTask();
    }
}

// ExampleBackgroundTask.h
#pragma once

#include "ExampleBackgroundTask.g.h"

namespace winrt::Tasks::implementation
{
    struct ExampleBackgroundTask : ExampleBackgroundTaskT<ExampleBackgroundTask>
    {
        ExampleBackgroundTask() = default;

        void Run(Windows::ApplicationModel::Background::IBackgroundTaskInstance const& taskInstance);
    };
}

namespace winrt::Tasks::factory_implementation
{
    struct ExampleBackgroundTask : ExampleBackgroundTaskT<ExampleBackgroundTask, implementation::ExampleBackgroundTask>
    {
    };
}

// ExampleBackgroundTask.cpp
#include "pch.h"
#include "ExampleBackgroundTask.h"

namespace winrt::Tasks::implementation
{
    void ExampleBackgroundTask::Run(Windows::ApplicationModel::Background::IBackgroundTaskInstance const& taskInstance)
    {
        throw hresult_not_implemented();
    }
}
// ExampleBackgroundTask.h
#pragma once

using namespace Windows::ApplicationModel::Background;

namespace Tasks
{
    public ref class ExampleBackgroundTask sealed : public IBackgroundTask
    {

    public:
        ExampleBackgroundTask();

        virtual void Run(IBackgroundTaskInstance^ taskInstance);
        void OnCompleted(
            BackgroundTaskRegistration^ task,
            BackgroundTaskCompletedEventArgs^ args
        );
    };
}

// ExampleBackgroundTask.cpp
#include "ExampleBackgroundTask.h"

using namespace Tasks;

void ExampleBackgroundTask::Run(IBackgroundTaskInstance^ taskInstance)
{
}
  1. Si ejecutas algún código asincrónico en tu tarea en segundo plano, entonces esta necesita usar un aplazamiento. Si no usa un aplazamiento, el proceso de tarea en segundo plano puede finalizar inesperadamente si el método Run devuelve antes de que se haya ejecutado un trabajo asincrónico hasta su finalización.

Solicite el aplazamiento en el método Run antes de llamar al método asincrónico. Guarde el aplazamiento en un miembro de datos de clase para que se pueda tener acceso desde el método asincrónico. Declara el aplazamiento completo después de que se complete el código asincrónico.

El código de ejemplo siguiente obtiene el aplazamiento, lo guarda y lo libera cuando se completa el código asincrónico.

BackgroundTaskDeferral _deferral; // Note: defined at class scope so that 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();
}
// ExampleBackgroundTask.h
...
private:
    Windows::ApplicationModel::Background::BackgroundTaskDeferral m_deferral{ nullptr };

// ExampleBackgroundTask.cpp
...
Windows::Foundation::IAsyncAction ExampleBackgroundTask::Run(
    Windows::ApplicationModel::Background::IBackgroundTaskInstance const& taskInstance)
{
    m_deferral = taskInstance.GetDeferral(); // Note: defined at class scope so that we can mark it complete inside the OnCancel() callback if we choose to support cancellation.
    // TODO: Modify the following line of code to call a real async function.
    co_await ExampleCoroutineAsync(); // Run returns at this point, and resumes when ExampleCoroutineAsync completes.
    m_deferral.Complete();
}
void ExampleBackgroundTask::Run(IBackgroundTaskInstance^ taskInstance)
{
    m_deferral = taskInstance->GetDeferral(); // Note: defined at class scope so that we can mark it complete inside the OnCancel() callback if we choose to support cancellation.

    //
    // TODO: Modify the following line of code to call a real async function.
    //       Note that the task<void> return type applies only to async
    //       actions. If you need to call an async operation instead, replace
    //       task<void> with the correct return type.
    //
    task<void> myTask(ExampleFunctionAsync());

    myTask.then([=]() {
        m_deferral->Complete();
    });
}

Nota

En C#, se llama a los métodos asincrónicos de la tarea en segundo plano mediante las palabras clave async/await. En C++/CX, se puede lograr un resultado similar mediante una cadena de tareas.

Para más información acerca de los modelos asincrónicos, consulta el tema sobre la Programación asincrónica. Para obtener más ejemplos sobre cómo usar aplazamientos para evitar que una tarea en segundo plano termine antes de tiempo, consulta la muestra de tarea en segundo plano.

Los siguientes pasos se completan en una de tus clases de aplicaciones (por ejemplo, MainPage.xaml.cs).

Nota

También puede crear una función dedicada al registro de tareas en segundo plano; consulte Registrar una tarea en segundo plano. En ese caso, en lugar de usar los tres pasos siguientes, puede simplemente construir el desencadenador y proporcionarlo a la función de registro junto con el nombre de la tarea, el punto de entrada de la tarea y (opcionalmente) una condición.

Registrar la tarea en segundo plano por ejecutar

  1. Averigüe si la tarea en segundo plano ya está registrada mediante la iteración a través de la propiedad BackgroundTaskRegistration.AllTasks . Este paso es importante; si tu aplicación no comprueba los registros de tareas en segundo plano existentes, fácilmente podría registrar una tarea varias veces. Esto causaría problemas de rendimiento y consumiría el tiempo de CPU disponible para la tarea antes de que su trabajo pueda completarse.

En el ejemplo siguiente se recorre en iteración la propiedad AllTasks y se establece una variable de marca en true si la tarea ya está registrada.

var taskRegistered = false;
var exampleTaskName = "ExampleBackgroundTask";

foreach (var task in BackgroundTaskRegistration.AllTasks)
{
    if (task.Value.Name == exampleTaskName)
    {
        taskRegistered = true;
        break;
    }
}
std::wstring exampleTaskName{ L"ExampleBackgroundTask" };

auto allTasks{ Windows::ApplicationModel::Background::BackgroundTaskRegistration::AllTasks() };

bool taskRegistered{ false };
for (auto const& task : allTasks)
{
    if (task.Value().Name() == exampleTaskName)
    {
        taskRegistered = true;
        break;
    }
}

// The code in the next step goes here.
boolean taskRegistered = false;
Platform::String^ exampleTaskName = "ExampleBackgroundTask";

auto iter = BackgroundTaskRegistration::AllTasks->First();
auto hascur = iter->HasCurrent;

while (hascur)
{
    auto cur = iter->Current->Value;

    if(cur->Name == exampleTaskName)
    {
        taskRegistered = true;
        break;
    }

    hascur = iter->MoveNext();
}
  1. Si aún no está registrada, usa BackgroundTaskBuilder para crear una instancia de la tarea en segundo plano. El punto de entrada de la tarea debe ser el nombre de tu clase de tarea en segundo plano con el espacio de nombres como prefijo.

El desencadenador de tarea en segundo plano controla cuándo se ejecutará la tarea en segundo plano. Para obtener una lista de posibles desencadenadores del sistema, consulta SystemTrigger.

Por ejemplo, este código crea una nueva tarea en segundo plano y la establece para que se ejecute cuando se produce el desencadenador TimeZoneChanged :

var builder = new BackgroundTaskBuilder();

builder.Name = exampleTaskName;
builder.TaskEntryPoint = "Tasks.ExampleBackgroundTask";
builder.SetTrigger(new SystemTrigger(SystemTriggerType.TimeZoneChange, false));
if (!taskRegistered)
{
    Windows::ApplicationModel::Background::BackgroundTaskBuilder builder;
    builder.Name(exampleTaskName);
    builder.TaskEntryPoint(L"Tasks.ExampleBackgroundTask");
    builder.SetTrigger(Windows::ApplicationModel::Background::SystemTrigger{
        Windows::ApplicationModel::Background::SystemTriggerType::TimeZoneChange, false });
    // The code in the next step goes here.
}
auto builder = ref new BackgroundTaskBuilder();

builder->Name = exampleTaskName;
builder->TaskEntryPoint = "Tasks.ExampleBackgroundTask";
builder->SetTrigger(ref new SystemTrigger(SystemTriggerType::TimeZoneChange, false));
  1. Puedes agregar una condición para controlar si la tarea se ejecutará después de que se produzca el evento del desencadenador (opcional). Por ejemplo, si no quieres que la tarea se ejecute hasta que el usuario esté presente, usa la condición UserPresent. Para obtener una lista de posibles condiciones, consulta SystemConditionType.

El siguiente código de muestra asigna una condición que requiere que el usuario esté presente:

builder.AddCondition(new SystemCondition(SystemConditionType.UserPresent));
builder.AddCondition(Windows::ApplicationModel::Background::SystemCondition{ Windows::ApplicationModel::Background::SystemConditionType::UserPresent });
// The code in the next step goes here.
builder->AddCondition(ref new SystemCondition(SystemConditionType::UserPresent));
  1. Registra la tarea en segundo plano llamando al método Register en el objeto BackgroundTaskBuilder. Almacena el resultado de BackgroundTaskRegistration para usarlo en el siguiente paso.

El siguiente código registra la tarea en segundo plano y almacena el resultado:

BackgroundTaskRegistration task = builder.Register();
Windows::ApplicationModel::Background::BackgroundTaskRegistration task{ builder.Register() };
BackgroundTaskRegistration^ task = builder->Register();

Nota

Las aplicaciones universales de Windows deben llamar a RequestAccessAsync antes de registrar cualquier tipo de desencadenador en segundo plano.

Para garantizar que tu aplicación universal de Windows continúe funcionando correctamente después de publicar una actualización, usa el desencadenador ServicingComplete (consulta SystemTriggerType) para realizar los cambios de configuración posteriores a la actualización como la migración de la base de datos de la aplicación y el registro de tareas en segundo plano. Es recomendable anular el registro de tareas en segundo plano asociadas con la versión anterior de la aplicación (consulta RemoveAccess) y registrar las tareas en segundo plano para la nueva versión de la aplicación (consulta RequestAccessAsync) en este momento.

Para obtener más información, vea Directrices para tareas en segundo plano.

Controlar la finalización de tareas en segundo plano mediante controladores de eventos

Debes registrar un método con BackgroundTaskCompletedEventHandler, de manera que tu aplicación pueda obtener resultados de la tarea en segundo plano. Cuando se inicia o reanuda la aplicación, se llamará al método marcado si la tarea en segundo plano se ha completado desde la última vez que la aplicación estaba en primer plano. (Se llamará de forma inmediata al método OnCompleted si la tarea en segundo plano se completa mientras tu aplicación se encuentra actualmente en primer plano).

  1. Escribe un método OnCompleted para administrar la finalización de tareas en segundo plano. Por ejemplo, el resultado de la tarea en segundo plano podría provocar una actualización de la interfaz de usuario. La superficie del método que se muestra aquí es necesaria para el método del controlador de eventos OnCompleted, incluso aunque este ejemplo no usa el parámetro args.

La siguiente muestra de código reconoce la finalización de la tarea en segundo plano y llama a un método de ejemplo de actualización de la interfaz de usuario que toma una cadena de mensaje.

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);
}
void UpdateUI(winrt::hstring const& message)
{
    MyTextBlock().Dispatcher().RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, [=]()
    {
        MyTextBlock().Text(message);
    });
}

void OnCompleted(
    Windows::ApplicationModel::Background::BackgroundTaskRegistration const& sender,
    Windows::ApplicationModel::Background::BackgroundTaskCompletedEventArgs const& /* args */)
{
	// You'll previously have inserted this key into local settings.
    auto settings{ Windows::Storage::ApplicationData::Current().LocalSettings().Values() };
    auto key{ winrt::to_hstring(sender.TaskId()) };
    auto message{ winrt::unbox_value<winrt::hstring>(settings.Lookup(key)) };

    UpdateUI(message);
}
void MainPage::OnCompleted(BackgroundTaskRegistration^ task, BackgroundTaskCompletedEventArgs^ args)
{
    auto settings = ApplicationData::Current->LocalSettings->Values;
    auto key = task->TaskId.ToString();
    auto message = dynamic_cast<String^>(settings->Lookup(key));
    UpdateUI(message);
}

Nota

Las actualizaciones de la interfaz de usuario se deberían realizar de forma asincrónica, para evitar retener el subproceso de interfaz de usuario. Para ver un ejemplo, consulta el método UpdateUI en la muestra de tarea en segundo plano.

  1. Vuelve al punto en el que registraste la tarea en segundo plano. Después de la línea de código, agrega un nuevo objeto BackgroundTaskCompletedEventHandler. Proporciona tu método OnCompleted como parámetro para el constructor BackgroundTaskCompletedEventHandler.

La siguiente muestra de código agrega un constructor BackgroundTaskCompletedEventHandler a BackgroundTaskRegistration:

task.Completed += new BackgroundTaskCompletedEventHandler(OnCompleted);
task.Completed({ this, &MainPage::OnCompleted });
task->Completed += ref new BackgroundTaskCompletedEventHandler(this, &MainPage::OnCompleted);

Declarar en el manifiesto de la aplicación que la aplicación usa tareas en segundo plano

Antes de que tu aplicación pueda ejecutar tareas en segundo plano, debes declarar cada una de ellas en el manifiesto de la aplicación. Si la aplicación intenta registrar una tarea en segundo plano con un desencadenador que no aparece en el manifiesto, el registro de la tarea en segundo plano producirá un error de "clase en tiempo de ejecución no registrada".

  1. Abre el diseñador de manifiestos del paquete. Para ello, abre el archivo Package.appxmanifest.
  2. Abre la pestaña Declaraciones.
  3. Desde la lista desplegable Declaraciones disponibles, selecciona Tareas en segundo plano y haz clic en Agregar.
  4. Activa la casilla Evento del sistema.
  5. En el cuadro de texto Punto de entrada: , escriba el espacio de nombres y el nombre de la clase en segundo plano, que es para este ejemplo Tasks.ExampleBackgroundTask.
  6. Cierra el diseñador de manifiesto.

Se añadirá el siguiente elemento Extensions al archivo Package.appxmanifest para registrar la tarea en segundo plano:

<Extensions>
  <Extension Category="windows.backgroundTasks" EntryPoint="Tasks.ExampleBackgroundTask">
    <BackgroundTasks>
      <Task Type="systemEvent" />
    </BackgroundTasks>
  </Extension>
</Extensions>

Resumen y pasos siguientes

Ahora deberías conocer los conceptos básicos de cómo escribir una clase de tareas en segundo plano, cómo registrar la tarea en segundo plano desde dentro de tu aplicación y cómo hacer que tu aplicación reconozca cuándo se ha completado la tarea en segundo plano. También deberías comprender cómo actualizar el manifiesto de la aplicación para que la aplicación pueda registrar correctamente la tarea en segundo plano.

Nota:

Descarga la muestra de tarea en segundo plano para ver ejemplos de código similares en el contexto de una aplicación para UWP completa y robusta que usa tareas en segundo plano.

Consulta los siguientes temas relacionados para obtener referencia de las API, una guía conceptual sobre tareas en segundo plano e instrucciones más detalladas para escribir aplicaciones que usan tareas en segundo plano.

Temas con instrucciones detalladas sobre las tareas en segundo plano

Guía de tareas en segundo plano

Referencia de API de tareas en segundo plano