Summary of Chapter 20. Async and file I/O
A graphical user interface must respond to user-input events sequentially. This implies that all processing of user-input events must occur in a single thread, often called the main thread or the UI thread.
Users expect graphical user interfaces to be responsive. This means that a program must process user-input events quickly. If that is not possible, then processing must be relegated to secondary threads of execution.
Several sample programs in this book have used the
WebRequest class. In this class the
BeginGetReponse method starts a worker thread, which calls a callback function when it is complete. However, that callback function runs in the worker thread, so the program must call
Device.BeginInvokeOnMainThread method to access the user interface.
A more modern approach to asynchronous processing is available in .NET and C#. This involves the
Task<TResult> classes, and other types in the
System.Threading.Tasks namespaces, as well as the C# 5.0
await keywords. That's what this chapter focuses on.
From callbacks to await
Page class itself contains three asynchronous methods to display alert boxes:
Task objects indicate that these methods implement the Task-based Asynchronous Pattern, known as TAP. These
Task objects are returned quickly from the method. The
Task<T> return values constitute a "promise" that a value of type
TResult will be available when the task completes. The
Task return value indicates an asynchronous action that will complete but with no value returned.
In all these cases, the
Task is complete when the user dismisses the alert box.
An alert with callbacks
The AlertCallbacks sample demonstrates how to handle
Task<bool> return objects and
Device.BeginInvokeOnMainThread calls using callback methods.
An alert with lambdas
sample demonstrates how to use anonymous lambda functions for handling
An alert with await
A more straightforward approach involves the
await keywords introduced in C# 5. The AlertAwait
sample demonstrates their use.
An alert with nothing
If the asynchronous method returns
Task rather than
Task<TResult>, then the program doesn't need to use any of these techniques if it doesn't need to know when the asynchronous task completes. The
sample demonstrates this.
Saving program settings asynchronously
A platform-independent timer
Traditionally, the .NET
System.IO namespace has been the source of file I/O support. Although some methods in this namespace support asynchronous operations, most do not. The namespace also supports several simple method calls that perform sophisticated file I/O functions.
Good news and bad news
All the platforms supported by Xamarin.Forms support application local storage — storage that is private to the application.
The Xamarin.iOS and Xamarin.Android libraries include a version of .NET that Xamarin has expressly tailored for these two platforms. These include classes from
System.IO that you can use to perform file I/O with application local storage in these two platforms.
However, if you search for these
System.IO classes in a Xamarin.Forms PCL, you won't find them. The problem is that Microsoft completely revamped file I/O for the Windows Runtime API. Programs targeting Windows 8.1, Windows Phone 8.1, and the Universal Windows Platform do not use
System.IO for file I/O.
A first shot at cross-platform file I/O
TextFileTryout sample defines an
IFileHelper interface for file I/O, and implementations of this interface in all the platforms. However, the Windows Runtime implementations don't work with the methods in this interface because the Windows Runtime file I/O methods are asynchronous.
Accommodating Windows Runtime file I/O
Programs running under the Windows Runtime use classes in the
Windows.Storage.Streams namespaces for file I/O, including application local storage. Because Microsoft determined that any operation requiring more than 50 milliseconds should be asynchronous to avoid blocking the UI thread, these file I/O methods are mostly asynchronous.
The code demonstrating this new approach will be in a library so that it can be used by other applications.
It's advantageous to store reusable code in libraries. This is obviously more difficult when different pieces of the reusable code are for entirely different operating systems.
The Xamarin.FormsBook.Platform solution demonstrates one approach. This solution contains seven different projects:
- Xamarin.FormsBook.Platform, a normal Xamarin.Forms PCL
- Xamarin.FormsBook.Platform.iOS, an iOS class library
- Xamarin.FormsBook.Platform.Android, an Android class library
- Xamarin.FormsBook.Platform.UWP, a Universal Windows class library
- Xamarin.FormsBook.Platform.Windows, a PCL for Windows 8.1.
- Xamarin.FormsBook.Platform.WinPhone, a PCL for Windows Phone 8.1
- Xamarin.FormsBook.Platform.WinRT, a shared project for code that is common to all the Windows platforms
All the individual platform projects (with the exception of Xamarin.FormsBook.Platform.WinRT) have references to Xamarin.FormsBook.Platform. The three Windows projects have a reference to Xamarin.FormsBook.Platform.WinRT.
All the projects contain a static
Toolkit.Init method to ensure that the library is loaded if it's not directly referenced by a project in a Xamarin.Forms application solution.
The Xamarin.FormsBook.Platform project contains the new
IFileHelper interface. All the methods now have names with
Async suffixes and return
The Xamarin.FormsBook.Platform.WinRT project contains the
FileHelper class for the Windows Runtime.
The Xamarin.FormsBook.Platform.iOS project contains the
FileHelper class for iOS. These methods must now be asynchronous. Some of the methods use the asynchronous versions of methods defined in
ReadToEndAsync. Others convert a result to a
Task object using the
The Xamarin.FormsBook.Platform.Android project contains a similar
FileHelper class for Android.
The Xamarin.FormsBook.Platform project also contains a
FileHelper class that eases the use of the
To use these libraries, an application solution must include all the projects in the Xamarin.FormsBook.Platform solution, and each of the application projects must have a reference to the corresponding library in Xamarin.FormsBook.Platform.
The TextFileAsync solution demonstrates how to use the Xamarin.FormsBook.Platform libraries. Each of the projects has a call to
Toolkit.Init. The application makes use of the asynchronous file I/O functions.
Keeping it in the background
Methods in libraries that make calls to multiple asynchronous methods — such as the
ReadFileASync methods in the Windows Runtime
FileHelper class — can be made somewhat more efficient by using the
ConfigureAwait method to avoid switching back to the user-interface thread.
Don't block the UI thread!
Sometimes it's tempting to avoid the use of
await by using the
Result property on the methods. This should be avoided for it can block the UI thread or even hang the application.
Your own awaitable methods
You can run some code asynchronously by passing it to one of the
Task.Run methods. You can call
Task.Run within an async method that handles some of the overhead.
Task.Run patterns are discussed below.
The basic Mandelbrot set
To report progress from an asynchronous method, you can instantiate a
Progress<T> class and define your asynchronous method to have an argument of type
IProgress<T>. This is demonstrated in the MandelbrotProgress sample.
Cancelling the job
You can also write an asynchronous method to be cancellable. You begin with a class named
Token property is a value of type
CancellationToken. This is passed to the asynchronous function. A program calls the
Cancel method of
CancellationTokenSource (generally in response to an action by the user) to cancel the asynchronous function.
The asynchronous method can periodically check the
IsCancellationRequested property of
CancellationToken and exit if the property is
true, or simply call the
ThrowIfCancellationRequested method, in which case the method ends with an
The MandelbrotCancellation sample demonstrates the use of a cancellable function.
An MVVM Mandelbrot
Back to the web
WebRequest class used in some samples uses an old-fashioned asynchronous protocol called the Asynchronous Programming Model or APM. You can convert such a class to the modern TAP protocol using one of the
FromAsync methods in the
TaskFactory class. The
sample demonstrates this.