Generation App – Día 22 – Implementar agentes de segundo plano en tu aplicación

Los agentes de segundo plano y las tareas programadas realizan tareas en segundo plano, aun si la aplicación de primer plano no esta abierta. Los diferentes tipos de tareas programadas están diseñadas para los diferentes tipos de situaciones de proceso de segundo plano y por eso tienen diferentes comportamientos y restricciones. Aprende como implementar una aplicación que usa una tarea programada para registrar un agente de segundo plano. También entenderás la programación, duración y limitaciones de las tareas programadas.

Creando una aplicación que usa Tareas Programadas

Para crear una aplicación que usa tareas programadas, debes seguir los siguientes pasos:

  1. En Visual Studio, crea un nuevo proyecto Aplicación Windows Phone. Esta plantilla se encuentra en la categoría de Silverlight para Windows pone

  2. Ahora, agrega un proyecto de tipo Tarea Programada a tu solución. Del menúArchivo, selecciona Agregar -> Nuevo Proyecto. En la ventana de dialogo de Agregar Nuevo proyecto, selecciona Agente de Tareas Programadas Windows Phone. Deja el nombre por defecto, ScheduledTaskAgent1, y da clic a OK.

  3. En tu proyecto de primario, debes agregar una referencia hacia el proyecto tipo Agente. En el explorador de soluciones, realiza un clic derecho sobre el proyecto principal, y da clic a “Agregar Referencia…” y en la ventana de dialogo de Agregar Referencias, selecciona la pestana de Proyectos. Selecciona el proyecto tipo Agente, ScheduledTaskAgent1, y da clic a OK.

  4. En el explorador de soluciones, da doble clic a ScheduledAgent.cs del proyecto ScheduledTaskAgent1 para abrir el archivo. Veras que este archivo contiene la definición de una clase ScheduledAgent, el cual hereda la clase baseScheduledTaskAgent. Para este ejemplo, agrega la siguiente directiva para el namespace Shell y Sistema al tope del archivo.

    using Microsoft.Phone.Scheduler;
    using Microsoft.Phone.Shell;
    using System;
  5. Hay un método implementado en la clase, OnInvoke(ScheduledTask). Este método es invocado por el sistema operativo cuando la Tarea Programada es ejecutada. Aquí es donde deberías implementar el código que desees que se ejecute cuando el agente de segundo plano se ejecute. Cada aplicacion puede tener un solo ScheduledTaskAgent registrado a la vez, pero puedes programar este agente como un tipo de agente recurso-intensivo y periódico. Si tu aplicación usa ambas tareasResourceIntensiveTask y PeriodicTask, puedes verificar que tipo de objetoScheduledTask que es pasado al método OnInvoke para determinar cual tarea el agente esta siendo invocado y ramificar tu ejecución de código como desees. Si utilizas un solo tipo de agente, no necesitaras revisar el tipo de objeto ScheduledTask. En este ejemplo, el agente ejecuta un objeto ShellToast desde OnInvoke, indicando el tipo de rarea programada para al agente que fue llamado. Este Toast te mostrara cual agente se esta ejecutando. Sin embargo, no se mostrara mientras que la aplicación se encuentre en modo foreground. Cuando el código de la tarea programada se haya completado, deberás invocar NotifyComplete()para informarle al sistema operativo que ya no necesitas seguir ejecutándolo. Esto permite al sistema operativo lograr gestionar otros agentes programados.

    protected override void OnInvoke(ScheduledTask task)
    {
      //TODO: Add code to perform your task in background
      string toastMessage = "";
    
      // If your application uses both PeriodicTask and ResourceIntensiveTask
      // you can branch your application code here. Otherwise, you don't need to.
      if (task is PeriodicTask)
      {
        // Execute periodic task actions here.
        toastMessage = "Periodic task running.";
      }
      else
      {
        // Execute resource-intensive task actions here.
        toastMessage = "Resource-intensive task running.";
      }
    
      // Launch a toast to show that the agent is running.
      // The toast will not be shown if the foreground application is running.
      ShellToast toast = new ShellToast();
      toast.Title = "Background Agent Sample";
      toast.Content = toastMessage;
      toast.Show();
    
      // If debugging is enabled, launch the agent again in one minute.
    #if DEBUG_AGENT
      ScheduledActionService.LaunchForTest(task.Name, TimeSpan.FromSeconds(60));
    #endif
    
      // Call NotifyComplete to let the system know the agent is done working.
      NotifyComplete();
    }
  6. Ahora en los siguientes pasos, la aplicación de primer plano es modificada para permitir a los usuarios para habilitar y deshabilitar los agentes periódicos y recurso-intensivos. Primero abre el archivo MainPage.xamly pega el siguiente código XAML dentro del Grid “ContentPanel”

    Este código agrega dos conjuntos de controles, uno para cada tipo de agente. La mayoría de los controles son text blocks que estarán enlazados con los objetosScheduledTask que representan los agentes de segundo plano, permitiéndote observar las propiedades de estos objetos. Además, un check box es agregado por cada tipo de agente que muestra y permite que el usuario habilite o deshabilite el estado de cada agente. Manejadores de eventos Checked Unchecked, los cuales son utilizados para apagar y prender los agentes.

  7. En MainPage.xaml.cs, agrega la siguiente directiva en el tope de la pagina:

    using Microsoft.Phone.Scheduler;
  8. Crea dos variables que representaran cada tipo de agente. Estos serán objetos que estarán enlazados a la interfaz de usuario. El servicio de acción programada identifica únicamente tareas programadas por su propiedad Name (Nombre). Crea dos variables conteniendo los nombres que se usaran para los agentes.  Crea una variable booleana para usar como determinar si algún agente de segundo plano haya sido deshabilitado por el usuario final. Agrega las siguiente líneas de código dentro de la definición de la clase:

    public partial class MainPage : PhoneApplicationPage
    {
      PeriodicTask periodicTask;
      ResourceIntensiveTask resourceIntensiveTask;
    
      string periodicTaskName = "PeriodicAgent";
      string resourceIntensiveTaskName = "ResourceIntensiveAgent";
      public bool agentsAreEnabled = true;
  9. Ahora, implementa un método helper llamado StartPeriodicAgent. Este método primero usa un método Find(String) para obtener la referencia de la tarea periódica con el nombre especificado. Si el objeto de tarea programada no es null, entonces podrás invocar Remove(String) para excluir el registro del sistema. No puedes actualizar agentes directamente. Deberás remover y luego agregar. Luego, crea un nuevo objeto PeriodicTask y asignar su nombre en el constructor.

    Ahora establece la propiedad Descripción. Esta propiedad es requerida para agentes periódicos y es usado para describir el agente al usuario en la pagina de configuración de tareas de segundo plano en el dispositivo. Luego, invocaAdd(ScheduledAction) para registrar el agente periódico  en el sistema. Una excepción InvalidaOperationException  es lanzado cuando Add es invocado si el usuario ha deshabilitado agentes de segundo plano por la aplicación, por ende es importante utilizar un bloque try. Si la llamada es exitosa, establece el contexto de datos del elemento de la interfaz de usuario asociada y mostrar las propiedades del objeto al usuario. Si la llamada lanza una excepción, verifica el mensaje de la excepción. Si el mensaje dice “BNS Error: The action is disabled”, entonces deberías alertar al usuario que los agentes de segundo plano están deshabilitados.El métodoLaunchForTest(String, TimeSpan) es incluido para ejecutar el agente un minuto después que este método es invocado.  Antes de la llamada anterior a este método, se coloca un bloque #If para permitir que la aplicación se intercambie fácilmente entre modos de depuración y producción. Para habilitar este método, incluye la siguiente línea al tope de MainPage.xaml.cs:

    #define DEBUG_AGENT

    Ahora pega el siguiente método en la definición de la clase MainPage

    private void StartPeriodicAgent()
    {
      // Variable for tracking enabled status of background agents for this app.
      agentsAreEnabled = true;
    
      // Obtain a reference to the period task, if one exists
      periodicTask = ScheduledActionService.Find(periodicTaskName) as PeriodicTask;
    
      // If the task already exists and background agents are enabled for the
      // application, you must remove the task and then add it again to update 
      // the schedule
      if (periodicTask != null)
      {
        RemoveAgent(periodicTaskName);
      }
    
      periodicTask = new PeriodicTask(periodicTaskName);
    
      // The description is required for periodic agents. This is the string that the user
      // will see in the background services Settings page on the device.
      periodicTask.Description = "This demonstrates a periodic task.";
    
      // Place the call to Add in a try block in case the user has disabled agents.
      try
      {
        ScheduledActionService.Add(periodicTask);
        PeriodicStackPanel.DataContext = periodicTask;
    
        // If debugging is enabled, use LaunchForTest to launch the agent in one minute.
    #if(DEBUG_AGENT)
        ScheduledActionService.LaunchForTest(periodicTaskName, TimeSpan.FromSeconds(60));
    #endif
      }
      catch (InvalidOperationException exception)
      {
        if (exception.Message.Contains("BNS Error: The action is disabled"))
        {
          MessageBox.Show("Background agents for this application have been disabled by the user.");
          agentsAreEnabled = false;
          PeriodicCheckBox.IsChecked = false;
        }
    
        if (exception.Message.Contains("BNS Error: The maximum number of ScheduledActions of this type have already been added."))
        {
          // No user action required. The system prompts the user when the hard limit of periodic tasks has been reached.
    
        }
        PeriodicCheckBox.IsChecked = false;
      }
      catch (SchedulerServiceException)
      {
        // No user action required.
        PeriodicCheckBox.IsChecked = false;
      }
    }

    NOTA: Agentes de segundo plano no son soportados en dispositivos de 256-MB de memoria RAM. Al intentar  trabajar con agentes en estos dispositivos arrojaran una excepción InvalidaOperationException. En lugar de manejar esta excepción en un bloque catch, tu aplicación debería verificar si se esta ejecutando en un dispositivo 256-MB antes de agregar un agente.

  10. Ahora implementaremos el método helper para la tarea tipo recurso-intensivo. Este método es idéntico al método para el agente periódico a excepción que este utiliza la clase ResoucerIntensiveTaskpara programar el agente y otro nombre es utilizado.

    private void StartResourceIntensiveAgent()
    {
      // Variable for tracking enabled status of background agents for this app.
      agentsAreEnabled = true;
    
      resourceIntensiveTask = ScheduledActionService.Find(resourceIntensiveTaskName) as ResourceIntensiveTask;
    
      // If the task already exists and background agents are enabled for the
      // application, you must remove the task and then add it again to update 
      // the schedule.
      if (resourceIntensiveTask != null)
      {
        RemoveAgent(resourceIntensiveTaskName);
      }
    
      resourceIntensiveTask = new ResourceIntensiveTask(resourceIntensiveTaskName);
    
      // The description is required for periodic agents. This is the string that the user
      // will see in the background services Settings page on the device.
      resourceIntensiveTask.Description = "This demonstrates a resource-intensive task.";
    
      // Place the call to Add in a try block in case the user has disabled agents.
      try
      {
        ScheduledActionService.Add(resourceIntensiveTask);
        ResourceIntensiveStackPanel.DataContext = resourceIntensiveTask;
    
        // If debugging is enabled, use LaunchForTest to launch the agent in one minute.
    #if(DEBUG_AGENT)
        ScheduledActionService.LaunchForTest(resourceIntensiveTaskName, TimeSpan.FromSeconds(60));
    #endif
      }
      catch (InvalidOperationException exception)
      {
        if (exception.Message.Contains("BNS Error: The action is disabled"))
        {
          MessageBox.Show("Background agents for this application have been disabled by the user.");
          agentsAreEnabled = false;
    
        }
        ResourceIntensiveCheckBox.IsChecked = false;
      }
      catch (SchedulerServiceException)
      {
        // No user action required.
        ResourceIntensiveCheckBox.IsChecked = false;
      }
    }
  11. Agrega una variable booleana a la clase, ignoreCheckBoxEvents. Esta variable será utilizada para habilitar o deshabilitar los eventos de CheckBox al inicializar la pagina.

    bool ignoreCheckBoxEvents = false;
  12. Ahora, agrega manejadores de eventos Checked y Unchecked para los controlesCheckBox. Estos manejadores invocan los métodos Start y Stop previamente implementados, para ejecutar y detener los agentes. Si ignoreCheckBoxEventsesta en verdadero, los manejadores de eventos serán retornados sin realizar ninguna función.

    private void PeriodicCheckBox_Checked(object sender, RoutedEventArgs e)
    {
      if (ignoreCheckBoxEvents) 
        return;
      StartPeriodicAgent();
    }
    private void PeriodicCheckBox_Unchecked(object sender, RoutedEventArgs e)
    {
      if (ignoreCheckBoxEvents)
       return;
      RemoveAgent(periodicTaskName);
    }
    private void ResourceIntensiveCheckBox_Checked(object sender, RoutedEventArgs e)
    {
      if (ignoreCheckBoxEvents)
        return;
      StartResourceIntensiveAgent();
    }
    private void ResourceIntensiveCheckBox_Unchecked(object sender, RoutedEventArgs e)
    {
      if (ignoreCheckBoxEvents)
        return;
      RemoveAgent(resourceIntensiveTaskName);
    }
  13. El método RemoveAgent simplemente invoca Remove(String) dentro de un bloque Try para evitar que alguna excepción altere el comportamiento de la aplicación.

    private void RemoveAgent(string name)
    {
      try
      {
        ScheduledActionService.Remove(name);
      }
      catch (Exception)
      {
      }
    }
  14. El paso final es hacer override el métodoOnNavigatedTo(NavigationEventArgs) de la clase PhoneApplicationPage. Este método es invocado cada vez que el usuario navega a esta pagina. Ahora debes asignar la variable booleana ignoreCheckBoxEvents

    a verdadero para que los manejadores de eventos de los CheckBox estén esencialmente deshabilitados. Luego, usa el método Find(string) para verificar por agentes tipo periódico o recurso-intensivo registrados en el sistema y actualizar los controles CheckBox para reflejar el estado actual de la aplicación. Finalmente, asigna el valor de la variable ignoreCheckBoxEventsa falso.

    protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
    {
      ignoreCheckBoxEvents = true;
    
      periodicTask = ScheduledActionService.Find(periodicTaskName) as PeriodicTask;
    
      if (periodicTask != null)
      {
        PeriodicStackPanel.DataContext = periodicTask;
      }
    
      resourceIntensiveTask = ScheduledActionService.Find(resourceIntensiveTaskName) as ResourceIntensiveTask;
      if (resourceIntensiveTask != null)
      {
        ResourceIntensiveStackPanel.DataContext = resourceIntensiveTask;
      }
    
      ignoreCheckBoxEvents = false;
    }

Autor: Jorge Ramirez      Síguelo en twitter @JorgeRamirezMSP
Publicación original –> http://j.mp/UGdcui
Fuente 1 –> http://j.mp/Si4iSX

Anuncios

1 comentario

Archivado bajo Windows Phone

Una respuesta a “Generation App – Día 22 – Implementar agentes de segundo plano en tu aplicación

  1. Pingback: Con Generation App nunca fue tan fácil desarrollar para Windows Phone « El Blog de MSP para Latinoamerica [BETA]

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s