Summary of Chapter 20. Async and file I/O
- PDF for offline use:
-
- Related Articles:
-
- Related Samples:
-
- Related Links:
-
Let us know how you feel about this.
0/250
last updated: 2016-10
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 and
Task<TResult> classes, and other types in the
System.Threading and
System.Threading.Tasks namespaces, as well as the C# 5.0
async and
await keywords. That's what this chapter focuses on.
From callbacks to await
The
Page class itself contains three asynchronous methods to display alert boxes:
DisplayAlertreturns a
Taskobject
DisplayAlertreturns a
Task<bool>object
DisplayActionSheetreturns a
Task<string>object
The
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
The AlertLambdas
sample demonstrates how to use anonymous lambda functions for handling
Task and
Device.BeginInvokeOnMainThread calls.
An alert with await
A more straightforward approach involves the
async and
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
NothingAlert
sample demonstrates this.
Saving program settings asynchronously
The SaveProgramChanges sample demonstrates the use of the
SavePropertiesAsync method of
Application to save program settings as they change without overriding the
OnSleep method.
A platform-independent timer
It's possible to use
Task.Delay to create a platform-independent timer. The
TaskDelayClock sample demonstrates this.
File input/output
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, and you can perform sophisticated file I/O functions using simple method calls.
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.
This means that you'll need to use the
DependencyService (first discussed in Chapter 9. Platform-specific API calls to implement file I/O.
A first shot at cross-platform file I/O
The
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 and
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.
Platform-specific libraries
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
Task objects.
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 StreamWriter and StreamReader:
WriteAsync and
ReadToEndAsync. Others convert a result to a
Task object using the
FromResult method.
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
DependencyService object.
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
WriteFileAsync and
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
ContinueWith or
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.
The various
Task.Run patterns are discussed below.
The basic Mandelbrot set
To draw the Mandelbrot set in real time, the Xamarin.Forms.Toolkit library has a
Complex structure similar to the one in the
System.Numerics namespace.
The MandelbrotSet sample has a
CalculateMandeblotAsync method in its code-behind file that calculates the basic black-and-white Mandelbrot set and uses
BmpMaker to put it on a bitmap.
Marking progress
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
CancellationTokenSource. The
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
OperationCancelledException.
The MandelbrotCancellation sample demonstrates the use of a cancellable function.
An MVVM Mandelbrot
The
MandelbrotXF sample has a more extensive user interface, and it's mostly based on a
MandelbrotModel and
MandelbrotViewModel classes:
Back to the web
The
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
ApmToTap
sample demonstrates.