Compartir a través de


Resumen del capítulo 20. Asincronía y E/S de archivos

Nota:

Este libro se publicó en la primavera de 2016 y no se ha actualizado desde entonces. Gran parte del libro sigue siendo útil, pero algunos de los materiales están anticuados y algunos temas ya no son completamente correctos o completos.

Una interfaz gráfica de usuario debe responder de forma secuencial a los eventos de entrada del usuario. Esto implica que todo el procesamiento de eventos de entrada del usuario debe realizarse en un único subproceso, a menudo denominado subproceso principal o subproceso de interfaz de usuario.

Los usuarios esperan que las interfaces gráficas de usuario respondan. Esto significa que un programa debe procesar rápidamente los eventos de entrada del usuario. Si eso no es posible, el procesamiento se debe relegar a subprocesos secundarios de ejecución.

Varios programas de ejemplo de este libro han utilizado la clase de WebRequest. En esta clase, el método BeginGetResponse inicia un subproceso de trabajo, que llama a una función de devolución de llamada cuando está completa. Sin embargo, esa función de devolución de llamada se ejecuta en el subproceso de trabajo, por lo que el programa debe llamar al método Device.BeginInvokeOnMainThread para tener acceso a la interfaz de usuario.

Nota:

Los programas de Xamarin.Forms deben usar HttpClient en lugar de WebRequest para acceder a los archivos a través de Internet. HttpClient admite operaciones asincrónicas.

Un enfoque más moderno para el procesamiento asincrónico está disponible en .NET y C#. Esto implica las clases Task y Task<TResult> y otros tipos en los espacios de nombres System.Threading y System.Threading.Tasks, así como las palabras clave async y await de C# 5.0. Esto es en lo que se centra este capítulo.

De las devoluciones de llamada a await

La propia clase Page contiene tres métodos asincrónicos para mostrar cuadros de alerta:

Los objetos Task indican que estos métodos implementan el modelo asincrónico basado en tareas, conocido como TAP. Estos objetos Task se devuelven rápidamente desde el método. Los valores devueltos de Task<T> constituyen una "promesa" de que un valor de tipo TResult estará disponible cuando se complete la tarea. El valor devuelto Task indica una acción asincrónica que se completará pero sin ningún valor devuelto.

En todos estos casos, Task se completa cuando el usuario descarta el cuadro de alerta.

Una alerta con devoluciones de llamada

En el ejemplo AlertCallbacks se muestra cómo controlar los objetos devueltos Task<bool> y las llamadas Device.BeginInvokeOnMainThread mediante métodos de devolución de llamada.

Una alerta con lambdas

En el ejemplo AlertLambdas se muestra cómo usar las funciones lambda anónimas para administrar llamadas Task y Device.BeginInvokeOnMainThread.

Una alerta con await

Un enfoque más sencillo implica las palabras clave async y await presentadas en C# 5. En el ejemplo AlertAwait se muestra su uso.

Una alerta con nothing

Si el método asincrónico devuelve Task en lugar de Task<TResult>, el programa no necesita usar ninguna de estas técnicas si no necesita saber cuándo se completa la tarea asincrónica. En el ejemplo NothingAlert se muestra esto.

Guardar la configuración del programa de forma asincrónica

En el ejemplo SaveProgramChanges se muestra el uso del método SavePropertiesAsync de Application para guardar la configuración del programa a medida que cambian sin reemplazar el método OnSleep.

Un temporizador independiente de la plataforma

Se puede usar Task.Delay para crear un temporizador independiente de la plataforma. En el ejemplo TaskDelayClock se muestra esto.

Entrada y salida de archivos

Tradicionalmente, el espacio de nombres System.IO de .NET ha sido el origen de la compatibilidad de E/S de archivos. Aunque algunos métodos de este espacio de nombres admiten operaciones asincrónicas, la mayoría no. El espacio de nombres también admite varias llamadas a métodos simples que realizan sofisticadas funciones de E/S de archivos.

Buenas y malas noticias

Todas las plataformas compatibles con Xamarin.Forms admiten el almacenamiento local de la aplicación que es privado para la aplicación.

Las bibliotecas de Xamarin.iOS y Xamarin.Android incluyen una versión de .NET que Xamarin ha adaptado expresamente para estas dos plataformas. Entre ellas se incluyen las clases de System.IO que puede usar para realizar la E/S de archivos con el almacenamiento local de la aplicación en estas dos plataformas.

Aun así, si busca estas clases System.IO en un PCL de Xamarin.Forms, no las encontrará. El problema es que Microsoft ha renovado por completo la E/S de archivos para la API de Windows Runtime. Los programas que tienen como destino Windows 8.1, Windows Phone 8.1 y la Plataforma universal de Windows no usan System.IO para la E/S de archivos.

Esto significa que necesitará usar DependencyService (que se describe en primer lugar en el Capítulo 9. Llamadas API específicas de la plataforma para implementar E/S de archivos.

Nota:

Las bibliotecas de clases portátiles se han reemplazado por las bibliotecas de .NET Standard 2.0, y .NET Standard 2.0 admite tipos System.IO para todas las plataformas Xamarin.Forms. Ya no es necesario usar DependencyService para la mayoría de las tareas de E/S de archivos. Consulte Control de archivos en Xamarin.Forms para obtener un enfoque más moderno para la E/S de archivos.

Primera captura de E/S de archivos entre plataformas

En el ejemplo TextFileTryout se define una interfaz IFileHelper para la E/S de archivos y las implementaciones de esta interfaz en todas las plataformas. Sin embargo, las implementaciones de Windows Runtime no funcionan con los métodos de esta interfaz porque los métodos de E/S de archivos de Windows Runtime son asincrónicos.

Adaptación de la E/S de archivos de Windows Runtime

Los programas que se ejecutan en Windows Runtime usan clases en los espacios de nombres Windows.Storage y Windows.Storage.Streams para la E/S de archivos, incluido el almacenamiento local de la aplicación. Dado que Microsoft ha determinado que cualquier operación que requiera más de 50 milisegundos debe ser asincrónica para evitar el bloqueo del subproceso de interfaz de usuario, estos métodos de E/S de archivos son principalmente asíncronicos.

El código que muestra este nuevo enfoque estará en una biblioteca para que lo puedan usar otras aplicaciones.

Bibliotecas específicas de plataforma

Es ventajoso almacenar código reutilizable en bibliotecas. Esto es obviamente más difícil cuando diferentes partes del código reutilizable son para sistemas operativos completamente diferentes.

En la solución Xamarin.FormsBook.Platform se muestra un enfoque. Esta solución contiene siete proyectos diferentes:

Todos los proyectos de plataforma individuales (a excepción de Xamarin.FormsBook.Platform.WinRT) tienen referencias a Xamarin.FormsBook.Platform. Los tres proyectos de Windows tienen una referencia a Xamarin.FormsBook.Platform.WinRT.

Todos los proyectos contienen un método estático Toolkit.Init para asegurarse de que la biblioteca se carga si un proyecto no hace referencia directa a ella en una solución de aplicación Xamarin.Forms.

El proyecto Xamarin.FormsBook.Platform contiene la nueva interfaz IFileHelper. Todos los métodos ahora tienen nombres con sufijos Async y devuelven objetos Task.

El proyecto Xamarin.FormsBook.Platform.WinRT contiene la clase FileHelper para Windows Runtime.

El proyectoXamarin.FormsBook.Platform.iOS contiene la clase FileHelper para iOS. Estos métodos deben ser ahora asincrónicos. Algunos de los métodos usan las versiones asincrónicas de los métodos definidos en StreamWriter y StreamReader: WriteAsync y ReadToEndAsync. Otros convierten un resultado en un objeto Task mediante el método FromResult.

El proyecto Xamarin.FormsBook.Platform.Android contiene una clase FileHelper similar para Android.

El proyecto Xamarin.FormsBook.Platform también contiene una clase FileHelper que facilita el uso del objeto DependencyService.

Para usar estas bibliotecas, una solución de aplicación debe incluir todos los proyectos de la solución Xamarin.FormsBook.Platform, y cada uno de los proyectos de aplicación debe tener una referencia a la biblioteca correspondiente en Xamarin.FormsBook.Platform.

La solución TextFileAsync muestra cómo usar las bibliotecas Xamarin.FormsBook.Platform. Cada uno de los proyectos tiene una llamada a Toolkit.Init. La aplicación hace uso de las funciones de E/S de archivos asincrónicas.

Mantener en segundo plano

Los métodos de las bibliotecas que realizan llamadas a varios métodos asincrónicos, como los métodos WriteFileAsync y ReadFileASync en la clase FileHelper de Windows Runtime, pueden resultar algo más eficaces mediante el método ConfigureAwait para evitar volver al subproceso de interfaz de usuario.

No bloquee el subproceso de interfaz de usuario.

A veces, es tentador evitar el uso de ContinueWith o await mediante la propiedad Result en los métodos. Esto se debe evitar para que pueda bloquear el subproceso de interfaz de usuario o incluso bloquear la aplicación.

Métodos propios que admiten await

Puede ejecutar código de forma asincrónica pasándolo a uno de los métodos Task.Run. Puede llamar a Task.Run dentro de un método asincrónico que controla parte de la sobrecarga.

A continuación se describen los distintos patrones de Task.Run.

Conjunto de Mandelbrot básico

Para dibujar el conjunto de Mandelbrot en tiempo real, la biblioteca Xamarin.Forms.Toolkit tiene una estructura Complex similar a la del espacio de nombres System.Numerics.

El ejemplo MandelbrotSet tiene un método CalculateMandeblotAsync en su archivo de código subyacente que calcula el conjunto de Mandelbrot básico en blanco y negro y utiliza BmpMaker para colocarlo en un mapa de bits.

Marcar el progreso

Para notificar el progreso de un método asincrónico, puede crear una instancia de una clase Progress<T> y definir el método asincrónico para tener un argumento de tipo IProgress<T>. Esto se muestra en el ejemplo MandelbrotProgress.

Cancelación del trabajo

También puede escribir un método asincrónico para que sea cancelable. Comienza con una clase denominada CancellationTokenSource. La propiedad Token es un valor de tipo CancellationToken. Esto se pasa a la función asincrónica. Un programa llama al método Cancel de CancellationTokenSource (generalmente en respuesta a una acción del usuario) para cancelar la función asincrónica.

El método asincrónico puede comprobar periódicamente la propiedad IsCancellationRequested de CancellationToken y salir si la propiedad es true, o simplemente llamar al método ThrowIfCancellationRequested, en cuyo caso el método finaliza con OperationCancelledException.

En el ejemplo MandelbrotCancellation se muestra el uso de una función que se puede cancelar.

Un conjunto Mandelbrot con MVVM

El ejemplo MandelbrotXF tiene una interfaz de usuario más extensa y se basa principal en las clases MandelbrotModel y MandelbrotViewModel:

Captura de pantalla triple de Mandelbrot X F

Volver a la Web

La clase WebRequest usada en algunos ejemplos utiliza un protocolo asincrónico antiguo denominado modelo de programación asincrónica o APM. Puede convertir una clase de este tipo en el protocolo TAP moderno mediante uno de los métodos FromAsync de la clase TaskFactory. El ejemplo ApmToTap muestra este comportamiento.