Categorías destacadas
programacion php    
Artículo
4
¡votar!

 Programación Asíncrona en Windows Phone 7

Una parte muy importante de la experiencia de usuario de nuestra aplicación móvil es la fluidez y rapidez con la que esta responde al usuario, todos hemos usado aplicaciones que ante cierta acción se quedan congeladas durante un periodo de tiempo antes de volver a responder, lo primero que pensamos es que la aplicación se ha bloqueado y la solución por la que optamos suele ser reiniciarla, aunque esperemos y la aplicación vuelva a ser operativa nuestra impresión hacia la usabilidad de la misma suele ser negativa. Además de esto, en Windows Phone 7 uno de los requerimientos para que nuestro desarrollo supere la aceptación en el marketplace es que la interface de usuario (UI) nunca se “congele”, siempre responda al usuario.

Para llevar a cabo esto, podemos hacer uso de distintos mecanismos de programación asíncrona incluidos en el framework de .NET 4, en esta ocasión vamos a ver como usar la clase BackgroundWorker para obtener de forma sencilla ejecución asíncrona en nuestra aplicación.

Hilos en Windows Phone 7

Toda aplicación Silverlight para WP7 se compone por defecto de dos Hilos:

Hilo de Interface de usuario: Es el hilo principal de nuestra aplicación, se encarga de manejar la entrada de usuario, procesar los objetos creados en XAML y ejecutar el resto de nuestro código.
Hilo de Composición: Se encarga de manejar tareas que normalmente serían responsabilidad del Hilo de UI, mejorando el rendimiento de las aplicaciones. Por ejemplo este hilo se encarga de procesar, combinar texturas gráficas y enviarlas a la GPU del dispositivo para que las dibuje, también se encarga de manejar las animaciones creadas con Storyboards, enviándolas automáticamente a la GPU y liberando así al hilo principal. Toda transformación realizada con los siguientes elementos se manejará en el hilo de composición:
ScaleTransform
TranslateTransform
RotateTransform
PlaneProjection
UIElement.Opacity
UIElement.Clip
Sin embargo el hilo de composición tiene limitaciones que pueden hacer que nuestra transformación pase a ejecutarse en el Hilo de UI, con el claro detrimento de rendimiento que experimentaríamos como consecuencia, para evitarlo en la medida de lo posible debemos tener en cuenta estos puntos:

Si para la Opacity usamos una máscara de opacidad, se procesará en el hilo de UI.
Si queremos hacer Clip de un área no rectangular, se procesará en el hilo de UI.
Si establecemos un ScaleTransform mayor al 50% del tamaño, se procesará en el hilo de UI.
Es muy importante que tengamos estos simples puntos en mente e intentemos evitarlos en la medida de lo posible, cada milisegundo ganado en el Hilo de Composición nos aportará mayor fluidez en nuestra interface de usuario.

BackgroundWorker

La clase BackgroundWorker es el método más sencillo para ejecutar código en un nuevo hilo. Tiene incluida funcionalidad para cancelar el procesamiento asíncrono, notificar el progreso, ejecutar código en el nuevo hilo y notificar la finalización de la tarea. Se encuentra definida en el namespace System.ComponentModel.

Es muy sencillo trabajar con esta clase, básicamente, debemos crear un manejador para el evento DoWork, este manejador es el que se ejecutará en el nuevo hilo dedicado, disponemos del evento ProgressChanged, que podemos manejar y lanzar durante el progreso de nuestra operación con el método ReportProgress. Una vez que la ejecución ha terminado se lanza el evento RunWorkerCompleted que de nuevo se ejecuta en nuestro hilo principal, en el que podemos comprobar si el proceso termino con errores, si se cancelo o si termino correctamente.

Durante la ejecución de nuestro código en el evento DoWork (asíncrono) desde el hilo principal podemos llamar al método CancelAsync, haciendo esto, si comprobamos la propiedad CancellationPending desde nuestro código asíncrono, obtendremos su valor a true, lo que siginifica que el usuario ha solicitado la cancelación, simplemente deberemos establecer a True la propiedad Cancel de DoWorkEventArgs y salir del método lo antes posible, momento en el que se lanzará el evento RunWorkerCompleted y, comprobando la propiedad Cancelled de RunWorkerCompletedEventArgs obtendremos true y podremos responder de forma adecuada.

Vamos a ver esto pasos con código para que quede más claro, tengo un pequeño ejemplo, una aplicación que cada 1,5 segundos añade un item a un listbox indicando el progreso de la tarea, usando para todo ello la clase BackgroundWorker:

Primero tenemos que inicializar una nueva instancia, indicandole que soportamos notificar progreso y cancelación (de lo contrario, al intentar cancelar o informar del progreso recibiremos una excepción de tipo InvalidOperationException), también vamos a crear los manejadores de eventos para DoWork, ProgressChanged y RunWorkerCompleted:
BackgroundWorker worker = new BackgroundWorker();
worker.WorkerSupportsCancellation = true;
worker.WorkerReportsProgress = true;
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
[code type="C#"]
A continuación incorporamos el código que deseemos ejecutar en el hilo asíncrono al evento DoWork (CUIDADO! este código no puede acceder directamente a la interface de usuario o recibiremos una excepción de Cross Threading inválido):
[code type="C#"]
void worker_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 1; i < 10; i++)
{
Thread.Sleep(1500);
worker.ReportProgress(i*10);
if (worker.CancellationPending)
{
e.Cancel = true;
return;
}
}
}

¿Y que pasa si queremos actualizar un control desde este código? Bueno, para esto WPF/Silverlight y por supuesto Windows Phone 7 ponen a nuestra disposición el Dispatcher, que básicamente nos sirve para indicar a un control que deseamos que ejecute código por nosotros, en el ejemplo anterior podriamos usar el Dispatcher de la ventana para que añada un Item a nuestro Listbox, en vez de usar el método ReportProgress:
this.Dispatcher.BeginInvoke((Action)delegate()
{
listCurrentOP.Items.Add("Elemento añadido con dispatcher.");
});

Simplemente estamos indicanto el código que queremos ejecutar, y la ventana se encargará de ejecutarlo por nosotros, además, al usar BeginInvoke (en Windows Phone 7 solo está disponible este método) la ejecución continua automáticamente sin esperar a que se ejecute el código indicado al Dispatcher, por lo que continuamos nuestra filosofía de que nuestra aplicación siempre sea “usable”.

Cada vez que llamamos al método ReportProgress pasandole un valor numérico se lanza el evento ProgressChanged en el que, por ejemplo, podemos actualizar un control progressbar que muestre al usuario el progreso de la tarea, ten en cuenta que este evento SI se ejecuta en el hilo principal, por lo que podrás acceder a los controles sin ningún problema de Cross Threading:
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
listCurrentOP.Items.Add(string.Format("Progreso: {0}.", e.ProgressPercentage));
}

Por último, una vez que el evento DoWork a terminado, se lanza el evento RunWorkerCompleted, donde podremos controlar posibles errores, cancelaciones o que todo ha funcionado correctamente:
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
StartWorker.IsEnabled = true;
if (e.Cancelled)
{
listCurrentOP.Items.Add("Cancelado por el usuario.");
return;
}
if (e.Error != null)
{
listCurrentOP.Items.Add("Error.");
return;
}
listCurrentOP.Items.Add("Completado.");
}

Y listo! ya tenemos ejecución asíncrona en nuestras operaciones más pesadas, con cancelación, notificación de progreso y notificación de terminación, además el uso de la clase BackgroundWorker no es nada complicado.



Os dejo para que podáis descargar el proyecto usado en este ejemplo, en Visual Studio 2010 para Windows Phone 7 aquí.

Un saludo a todos, gracias por leerme y Happy Coding!

   
Publicado por:
Josué Yeray Julián Ferreiro
Recomendar
a un amigo
Compartir
en redes
 
Comentarios
 
BBDD
Entornos de desarrollo
Entretenimiento
Herramientas
Internet
Lenguajes de script
Lenguajes imperativos
Lenguajes orientados a objeto
Otros lenguajes
Plataformas
Teoría
Varios
Copyright © 1998-2011 Programación en Castellano. Todos los derechos reservados
Datos legales | Politica de privacidad | Contacte con nosotros | Publicidad

Diseño web y desarrollo web. Un proyecto de los hermanos Carrero.

Red internet:
Juegos gratis | Servidores dedicados
Más internet: Password | Directorio de weblogs | Favicon