Hola a todos!
Vuelvo a la carga con el segundo artículo de la serie sobre código en segundo plano:
-
Tareas programadas.
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:
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:
{
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:
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:
{
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.
{
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:
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!
Hola a todos! Vamos a por el tercer artículo sobre ejecución de código en Background