Simple async file access using Windows 8, part 2

In the previous article I presented a helper library for reading and writing to the file system using the new Windows Runtime asynchronous file APIs.

One downside of the code I presented is that the actual reading and writing was still happening on the main UI thread. While this may not be a problem in a simple application, it is undesirable for complex documents.

The challenge is that we still need to be on the UI thread when we collect the data we want to save, but the writing itself could be on a background thread.

In this article I will cover a way to extend the API I presented previously, to allowing reading and writing via a background thread.

Reading on the background thread

One key tenet to reading in the background is that we need to load all of the data in before we update the UI. Think of a Microsoft Word document being loaded into memory in the background in its in-memory format, then only presenting it in the UI once the load has completed.

This is how we want to write our reading code:

FileAsync.Read<string>(KnownFolders.DocumentsLibrary, "myfile.txt",
    (fileSize, reader) => reader.ReadString(fileSize)  /* read in the background */,
    text => EditBox.Text = text  /* update the UI on the main thread */);

The example here is very simple, as our in memory “document” is a single string. In an application build using the MVVM pattern (Model-View-ViewModel), this would be a great way to load the Model in from a file, and finally bind it into the ViewModel once it has loaded through the two steps shown above.

Here is the implementation of the Read method:

public static async void Read<TDocument>(StorageFolder folder, string fileName,
Func<uint, DataReader, TDocument> reader, Action<TDocument> completion = null)
{
    StorageFile file;
    IRandomAccessStream stream;
    IInputStream inputStream;
    DataReader dr;
 
    file = await folder.GetFileAsync(fileName);
 
    stream = await file.OpenAsync(FileAccessMode.Read);
    inputStream = stream.GetInputStreamAt(0);
 
    uint fileSize = (uint)stream.Size;
 
    dr = new DataReader(inputStream);
    await dr.LoadAsync(fileSize);
 
    Task<TDocument> task = new Task<TDocument>(() => reader(fileSize, dr));
    task.Start();
    TDocument doc = await task;
 
    if (completion != null)
    {
        completion(doc);
    }
}

Here we used the new Task class, which is what underpins the await and async keywords.

Writing

Writing to the file involves creating the “document” on the UI thread (while may mean simply passing your Model if you are using MVVM). The Write method will then call you asynchronously to get the document written out.

FileAsync.Write<string>(KnownFolders.DocumentsLibrary, "myfile.txt",
   CreationCollisionOption.ReplaceExisting,
   EditBox.Text, /* pass in our "document" content */
    (w, doc) => w.WriteString(doc) /* write the "document" in the background */);

Much like the Read method, Write leverages Task to perform the writing.

public static async void Write<TDocument>(StorageFolder folder, string fileName,
    CreationCollisionOption collisionOption, TDocument doc,
    Action<DataWriter, TDocument> writer, Action<bool> complete = null)
{
    StorageFile creator;
    IRandomAccessStream stream;
    IOutputStream outputStream;
    DataWriter dw;
 
    creator = await folder.CreateFileAsync(fileName, collisionOption);
 
    stream = await creator.OpenAsync(FileAccessMode.ReadWrite);
    outputStream = stream.GetOutputStreamAt(0);
 
    dw = new DataWriter(outputStream);
 
    Task task = new Task(() => writer(dw, doc));
    task.Start();
    await task;
 
    await dw.StoreAsync();
    bool success = await outputStream.FlushAsync();
    if (complete != null)
    {
        complete(success);
    }
}

As you can see, the Windows Runtime makes it really easy to create asynchronously called code. With very little code we were able to take our earlier code, and with a little refactoring, enable the reading and writing to be fully performed on a background thread. Windows 8 should allow developers to create really great apps that stay responsive even when performing lengthy operations. When its this easy to achieve, this should become the rule rather than the exception.

Leave a Reply

*