[Windows Phone 8] Ejecución de código en segundo plano (II)

Hola a todos!

Vuelvo a la carga con el segundo artículo de la serie sobre código en segundo plano:

En esta ocasión nos toca hablar de las tareas programadas en segundo plano. En Windows Phone 8 podemos crear un tipo especial de proyecto llamado “Windows Phone Scheduled Task Agent”, este proyecto se podrá ejecutar cuando nuestra aplicación no esté activa, ejecutando código. Lo más importante es que comparte espacio de aplicación con nuestra app, por lo que podremos compartir archivos entre ambos proyectos de forma muy sencilla. Además, dispondremos de dos formas de registrar nuestro agente: Como una tarea intensiva o periódica. Su ejecución es la misma, pero contarán con diferentes limitaciones.

Pero no todo es precioso. Antes de ver como usar este tipo de proyecto, repasemos sus limitaciones:

Limitación Intensivo Periódico
Memoria (Sistema 1Gb) Máximo 20Mb hasta GDR2/ 25Mb en GDR3  Máximo 20Mb hasta GDR2/ 25Mb en GDR3
Memoria (Sistema 512Mb) Máximo 11Mb Máximo 11Mb
APIs Algunas APIs no están soportadas Algunas APIs no están soportadas
Caducidad Cada 2 semanas Cada 2 semanas
Desactivación por fallos Tras 2 fallos Tras 2 fallos
Periodo entre ejecuciones   30 minutos
Duración de ejecución 10 minutos 25 segundos
Batería Solo se ejecutan con +90% de batería.
Dispositivo conectado a red eléctrica.
Solo se paran si el dispositivo entra en ahorro de batería.
Conexión Solo pueden usar WiFi  
LockScreen Solo se ejecutan bajo lockscreen  
Límite de agentes por dispositivo   Varía por configuración, pero por encima de 6

Como podemos ver, esto no es como si nuestra aplicación estuviese en segundo plano constantemente. Existen muchas restricciones y debemos ser muy conscientes de ellas. La realidad nos dice que hay muy pocas aplicaciones que usen agentes intensivos. Casi siempre usaremos agentes periódicos. Tareas como actualizar el Live tile, descargar datos de un servicio web o comprobar datos, pueden ser realizadas sin problemas por este tipo de agentes.

Vamos al turrón! Empezamos a ensuciarnos las manos, creando un nuevo proyecto Windows Phone 8 normal. Con este proyecto se creará una solución, a la que añadiremos un proyecto del tipo “Windows Phone Scheduled Task Agent”, que encontraremos al final de la lista de proyectos de Windows Phone 8:

image

Una recomendación, no llaméis a vuestro proyecto ScheduledTask, ya que hay un tipo de dato que es precisamente ScheduledTask y tendréis que resolver conflictos de nombres. Llamadlo de alguna otra forma descriptiva.

Una vez que hemos creado el proyecto, por defecto nos añade una clase llamada ScheduledAgent, que hereda a su vez de la clase ScheduledTaskAgent. Esta clase base nos da métodos que sobre escribir para trabajar con nuestro agente. Por defecto se crea un constructor estático, que se suscribe al evento UnhandledException de la aplicación, y un método OnInvoke.

El método OnInvoke será el que se encargue de ejecutar nuestro código en background. Por defecto en este método vemos que se llama al método NotifyComplete. Este método notifica al sistema que hemos terminado el trabajo y no necesitamos seguir en ejecución. Si no lo llamamos, aunque se termine el método OnInvoke, el sistema no sabrá que hemos finalizado y matará el proceso pasado el tiempo máximo de ejecución.

Ahora vamos a añadir algo de código a nuestro agente, para este ejemplo vamos a actualizar el Live tile de la aplicación con una imagen y textos:

OnInvoke
protected override void OnInvoke(ScheduledTask task)
{
    var appTile = ShellTile.ActiveTiles.First();
    FlipTileData data = new FlipTileData();
    data.BackgroundImage = new Uri("http://...png", UriKind.Absolute);
    data.Title = "custom title";
    data.BackContent = "setting from background agent";
    appTile.Update(data);

    NotifyComplete();
}

A continuación, necesitamos que nuestra aplicación se encargue de registrar este agente. Cada aplicación solo puede tener un proyecto de ejecución en segundo plano. Debido a esta limitación, el sistema usará el ensamblado referenciado en nuestra aplicación como agente, por lo que lo primero que necesitamos hacer es añadir una referencia al agente en nuestra aplicación:

image

Una vez hecho esto, solo nos queda notificar al sistema que tenemos una tarea (periódica o intensiva) y que deseamos registrarla, indicando su nombre, descripción y fecha de caducidad. Como hemos dicho anteriormente, la fecha máxima de caducidad es de 2 semanas:

Background agent registration
private void RegisterBackgroundAgent()
{
    PeriodicTask task = (PeriodicTask)ScheduledActionService.Find("periodic");
    if (task == null)
    {
        task = new PeriodicTask("periodic");
        task.Description = "Periodic task to update app tile.";
        task.ExpirationTime = DateTime.Now.AddHours(1);
        ScheduledActionService.Add(task);
    }

#if DEBUG
    ScheduledActionService.LaunchForTest("periodic", new TimeSpan(0, 0, 30));
#endif
}

Si analizamos el código, podemos encontrar varias partes:

  • En primer lugar usamos la clase ScheduledActionService para buscar una tarea que se llame igual que la nuestra (periodic es un nombre descriptivo para una demo, pero no para una aplicación real, recuerdalo!)
  • Si no existe, la creamos. Aquí tenemos que indicar su nombre, que debe ser único, una descripción y un tiempo de caducidad. Por último añadimos nuestra nueva tarea periódica al ScheduledActionService. El registro de una tarea intensiva es exactamente el mismo, usando la clase ResourceIntensiveTask en lugar de PeriodicTask.
  • Por último, estaría bien poder probar nuestro agente sin tener que esperar media hora delante del emulador. para ello disponemos de un método LaunchForTest dentro de la clase ScheduledActionService. A este método le pasamos el nombre de la tarea que queremos probar y un TimeSpan con el tiempo que queremos que tarde en lanzarla. Si os fijáis, he envuelto esta llamada dentro de una condición de compilación, de forma que solo se ejecute en modo DEBUG. Esto es así porque el método LaunchForTest no puede ser usado en una app publicada, haría que nuestra aplicación fallase la certificación.

Por último, tenemos que ejecutar este código. Yo personalmente suelo ejecutarlo al iniciar la aplicación, al final del método InitializePhoneApplication. El Registro de un agente en background es muy rápido y no afectará al rendimiento, además es un código que no se ejecutará siempre, pues la mayor pate del tiempo la tarea ya estará registrada. En este sentido es importante darle al usuario el control sobre la activación, añadiendo una página de settings donde pueda activar o desactivar el agente.

InitializePhoneApplication
private void InitializePhoneApplication()
{
    if (phoneApplicationInitialized)
        return;

    // Create the frame but don't set it as RootVisual yet; this allows the splash
    // screen to remain active until the application is ready to render.
    RootFrame = new PhoneApplicationFrame();
    RootFrame.Navigated += CompleteInitializePhoneApplication;

    // Handle navigation failures
    RootFrame.NavigationFailed += RootFrame_NavigationFailed;

    // Handle reset requests for clearing the backstack
    RootFrame.Navigated += CheckForResetNavigation;

    // Ensure we don't initialize again
    phoneApplicationInitialized = true;

    RegisterBackgroundAgent();
}

Y listo, si ejecutamos el ejemplo, anclamos la aplicación al inicio y esperamos unos segundos (los que hayamos indicado, 30 en este caso) veremos que nuestro tile se actualiza:

image

Y con esto terminamos la segunda entrega de esta serie. Aquí os dejo el código fuente de este ejemplo, para que lo podáis usar como referencia. En el próximo artículo hablaremos sobre reproducción de música en background.

Un saludo y Happy Coding!

Published 29/1/2014 7:07 por Josué Yeray Julián Ferreiro
Comparte este post:

Comentarios

# [Windows Phone 8] Ejecución de código en segundo plano (III y Fin)

Monday, February 3, 2014 9:24 AM por Josue Yeray

Hola a todos! Vamos a por el tercer artículo sobre ejecución de código en Background