One of the best things you can do to create a great user experience in your app is to launch quickly. No doubt you’ve patiently (or impatiently!) watched a progress ring spin while waiting for some apps to come up. For apps that rely on web content, however, much of the startup time is spent waiting for HTTP requests to come back with that content. But of course, the user’s network speed and the responsiveness of the servers are out of your control. So can anything be done to improve startup performance?
Fortunately, yes! Starting with Windows 8.1, you can ask Windows to prefetch your most important content so it’s immediately available when you make the request. That is, prefetched content is cached locally so that the next time it’s requested through the Windows.Web.HttpClient APIs, Windows attempts to load it from the local HTTP cache before resorting to costlier web requests.
Web developers may already be familiar with the prefetch syntax that is part of the HTML5 specification. For native apps on Windows, the implementation follows a similar approach, using the ContentPrefetcher API.
Prefetching data can significantly boost your app’s startup speed (or the speed of any HTTP request) with just a few lines of code. To demonstrate the impact, we took a look at the Amazon shopping app that’s been designed to take full advantage of prefetching. Using Fiddler, we compared the loading times with and without prefetching enabled and found the app to load 1.17s faster on average with it enabled. That extra second might not sound like much, but it can be a huge differentiating factor in your app’s user experience, particularly when considering the amount of work that is required to achieve similar performance gains through other methods!
Note: Prefetching is not currently supported on Windows Phone.
How it works
Figure 1 illustrates how loading prefetched content works. The app submits requests to the Windows.Web.HttpClient, which first checks the local cache for the requested URIs. If that content is cached and has not expired, the API simply returns the data in the cache, thus saving precious time over making network requests. If the content is not cached or has expired, the API will request the content from the server.
Figure 1 – Requesting data with prefetch conditionally enabled
Windows 8.1 uses heuristics to predict app activations so that prefetched content can be loaded in time and only when certain system conditions are met, such as adequate battery and availability of network resources.
To include the feature in your project, just declare the URIs you wish to prefetch and let the platform take care of the rest. There are two ways to do this:
- Direct content (ContentPrefecther.ContentUriscollection). Use this for URIs that stay the same across app activations, though the referenced content on the server may change.
- Indirect content (ContentPrefetcher.IndirectContentUri property). Use this to register a fixed URI referencing a set of content URIs that change over time. For instance, if the URIs for news articles on the front page of your app change every 15 minutes, you can use the indirect content URI method to ensure only the latest content is prefetched.
Getting started
To make the most out of prefetching, prioritize larger content with a defined time-to-live in the Expires field of the HTTP response header. The TTL should be long enough such that prefetched content is still valid when the app runs. In addition, ensure your app uses the newer Windows.Web.HttpClient API because the prefetch service doesn’t support the .NET version in System.Net.WebClient.
Now let’s look at how each of the options works, as well as how to discover when content was successfully retrieved from the web. For more detail, you can always refer to the Content prefetch samplein the Windows SDK.
Registering direct content
Consider a scenario where you request the same URI each time your app is launched. Because the URI isn’t expected to change, simply add it to the ContentPrefetcher.ContentUris vector.
JavaScript
// init content uri to current weather data
var uri = new Windows.Foundation.Uri("http://my-newspaper.com/weather-data.json");
// register with the prefetcher
Windows.Networking.BackgroundTransfer.ContentPrefetcher.contentUris.append(uri);
C#
// init content uri to current weather data
var uri = new Windows.Foundation.Uri("http://my-newspaper.com/weather-data.json");
// register with the prefetcher
Windows.Networking.BackgroundTransfer.ContentPrefetcher.ContentUris.Append(uri);
Registering indirect content
The indirect content approach is best if your app requests a collection of URIs that changes over time, like a list of most recent articles. Instead of devising a complex procedure to adjust the ContentUris vector with new URIs to updated content, you instead provide the URI to an XML file that contains the desired URIs to prefetch through the ContentPrefetcher.IndirectContentUri property. This allows you to change the content URIs at any time, while the URI to the XML file remains fixed.
For example, consider the following XML (the format for which is described on the IndirectContentUri documentation):
<?xml version="1.0" encoding="utf-8"?>
<prefetchUris>
<uri>http://my-newspaper.com/articles/2014/4/22/foo.html</uri>
<uri>http://my-newspaper.com/articles/2014/4/22/bar.html</uri>
</prefetchUris>
Assuming that this XML is served by http://my-newspaper.com/recent-articles.xml, you can register it with the ContentPrefetcher as follows:
JavaScript
// init indirect uri to xml of dynamic content uris (urls to newest articles)
var uri = new Windows.Foundation.Uri("http://my-newspaper.com/recent-articles.xml");
// register with the prefetcher
Windows.Networking.BackgroundTransfer.ContentPrefetcher.indirectContentUri = uri;
C#
// init indirect uri to xml of dynamic content uris (urls to newest articles)
var uri = new Windows.Foundation.Uri("http://my-newspaper.com/recent-articles.xml");
// register with the prefetcher
Windows.Networking.BackgroundTransfer.ContentPrefetcher.IndirectContentUri = uri;
If you want to use this approach and haven’t decided on a web host, check out Azure services, such as web sites and mobile services. Creating a custom API for your XML service and/or scheduling a recurring taskmay be all you need to get started.
Accessing time of last prefetch
The last piece of the ContentPrefetcher class that you might find useful is the lastSuccessfulPrefetchTime property. Because Windows does not guarantee that it will prefetch every request, it’s also possible that it prefetched your content at some point, but more recently prefetched higher priority content for other apps. In that case, you can use lastSuccessfulPrefetchTime to check whether the content is fresh enough for your particular scenario:
JavaScript & C#
// get time of last successful prefetch
var prefetchTime = Windows.Networking.BackgroundTransfer.ContentPrefetcher.lastSuccessfulPrefetchTime;
If after checking this property you decide that you need to make a direct request, set the HttpCacheReadBehavior.mostRecent flag with the HttpClient object’s CacheControl to make sure you request the latest data.
Debugging
Event logging
For a closer look on how the ContentPrefetcher is prefetching content for your app, you can use the Event Viewer as shown in Figure 2. You can access it by typing “eventvwr” in a Run prompt or by typing “Event Viewer” right after hitting the Start key. Then you can find prefetch events under Applications and Services Logs > Microsoft > Windows > BackgroundTransfer-ContentPrefetcher. Be sure to right click on the operational log and enable it by selecting “Enable log” from the context menu.
In Figure 2, note how under the Task Category you can see whether an event was generated from an indirect URI (displayed as Indirect Content URI), or a simple content URI (displayed as URI fetch).
Figure 2 – Exploring prefetch events in the Event Viewer
Triggering prefetch manually
In addition to using the Event Viewer, starting with Update 2 of Visual Studio 2013, you can manually trigger prefetch events for your own experimentation. This can be particularly helpful when you want to compare your app’s startup duration with the feature enabled.
This is done using the Debug > Other Debug Targets > Trigger Windows Store App Prefetch menu command. For full details on this, refer to the Visual Studio blog post on triggering Prefetch for Windows Store Apps in Visual Studio 2013 Update 2.
Learning more
- ContentPrefetcher API documentation
- Content prefetch sample
- Building Great Service Connected Appsfrom //build 2013, by Matt Merry and Suhail Khalid
- Windows.Web.Http.HttpClient API documentation
- How to connect to an HTTP server using Windows.Web.Http
- HttpClient sample
- Five Great Reasons to Use the New HttpClient API to Connect to Web Services from //build 2013, by Peter Smith
Summary
If your app uses web content in any way, adding a couple of lines of code to register prefetchable URIs ensures that loading data from web services is one less thing your users have to wait for. Even if your app has to run a single async operation to wait for a request, you could always save precious time by prefetching it in advance.
Stelios Anagnostopoulos, Program Manager, Developer Ecosystem & Platform group