[This is the first in a five-part series on ways to help make your apps run better on lower-cost phones.–Ed.]
As Joe mentioned in his post, the introduction of lower cost devices and support for new markets brings with it an opportunity to reach a large new audience with your Windows Phone apps. Optimizing your apps for these lower cost devices is critical to ensure that your users get the best possible experience regardless of device.
The guidance for optimizing for lower cost Windows Phones is largely the same as for optimizing for today’s generation of devices. Apps that follow performance best practices today will run well across all devices without much effort. Apps that perform adequately today may perform noticeably slower on lower cost devices. To ensure your app performs well on lower cost devices as well as today’s devices, consider the following principles.
Optimize Startup Time
Be sure to keep startup time fast. The basic guidance here is to defer as much activity as possible until the first frame of your application is rendered. This means minimizing code in App/Page constructors and keeping Launching/OnNavigatedTo activity minimal.
If page initialization will take some time or is dependent on network requests or file I/O, for instance, then show a progress bar to convey activity. Leverage the system tray ProgressIndicator for the best performance. To leverage the system tray ProgressIndicator without affecting page layout, set the system tray Opacity to 0.
Once your first frame is rendered, keep blocking calls off of the UI thread. Isolated storage operations can block the UI thread if not done asynchronously. Keep these calls asynchronous to maintain a responsive UI at startup.
Adding a splash screen can additionally improve startup time since this frame is drawn by the OS itself as part of the launch sequence. Furthermore, simplifying and removing unnecessary XAML from your pages will reduce the impact of XAML parsing which can prolong startup significantly.
Reduce Memory Usage
Per certification requirement 5.2.5, apps should not exceed 90MB of memory usage on 256MB devices. With the introduction of 256MB devices like the Lumia 610, this becomes very relevant.
Use the memory profiler and memory-related API’s to profile your app’s memory usage. A basic rule of thumb is to load only what you need when you need it, and dispose items when they are no longer needed. Instead of preloading all game assets at startup, for instance, load assets for the on-screen experience only and flush those assets when the experience changes. Instead of loading full data sets in lists, load pages of data on demand as the user requests them.
Be careful of memory leaks. Apps which use a home button, for instance, can result in circular navigation loops which can fill the back stack with redundant page instances. These page instances consume memory and could result in your app crashing due to an OOM (out-of-memory) exception while users navigate through your app. In this case, avoid circular navigation loops by removing home button functionality or by leveraging the new back stack manipulation API’s exposed in Windows Phone 7.5.
Another common source of memory leaks is neglecting to deregister event handlers to static objects. If these event handlers aren’t deregistered, this can prevent objects from being reclaimed by the garbage collector. Imagine a search results page where the user navigates back and forth between the details of a search result and the search results page itself. If the details pages hold on to references to long-lived objects, they could continue to reside in memory even after the user navigates back from them. Be sure to deregister event handlers to static objects when they are no longer needed to prevent such leaks. OnRemovedFromJournal is the recommended place to perform this task since it handles page closure both via backward navigations as well as via the back stack manipulation API’s.
Also, be efficient with your use of images. Loading high resolution images into your UI and then scaling them down to fit in a small container will consume more memory than is necessary.
Handle Feature Reductions
To free up RAM for the foreground on 256MB devices, generic background agents (PeriodicTasks/ResourceIntensiveTasks) are disabled.
On today’s devices, users can disable background agents for an app manually via the Settings control panel, and the system can disable background agents for an app if the maximum number of supported background agents is exceeded. These conditions are surfaced to the app via an InvalidOperationException which the app must handle.
On 256MB devices, the app receives the same InvalidOperationException received in the maximum exceeded case above when trying to schedule a background agent (since the maximum number of supported background agents is 0). This means if an app is written today to handle the maximum exceeded case, it will continue to work on 256MB devices unchanged.
Make sure your apps handle this exception path and degrade gracefully when these features are unavailable. This will benefit your app experience both on today’s generation of devices as well as on new lower cost devices. The 256MB emulator introduced in the WPSDK 7.1.1 enables you to easily test this code path.
Additionally you may want to reduce/modify functionality on a low-memory device in order to reduce your app’s memory footprint. Using WebBrowserTask and BingMapsTask instead of the memory-intensive WebBrowser and Maps controls, for instance, can offload some of your app’s memory footprint to a native process, reducing the likelihood of your app running out of memory as users interact with it. If a customized map experience is critical to your app, adding fewer overlays to the Maps control on a low-memory device can help save some memory. Ultimately, these decisions should be made per app, as these reductions may be unnecessary if your apps already satisfy the 90MB certification requirement for 256MB devices.
Respond to User Input
A responsive UI is a basic expectation that users have of apps. The basic guidance here is to keep as much activity off of the UI thread as possible until absolutely necessary.
To keep page load times and in-app navigation responsive, defer loading activities until the first frame is rendered. The guidance for optimizing startup time of your MainPage (above) applies to optimizing the startup time of subsequent page loads as well. Additionally, leverage the TiltEffect to acknowledge user input. This will make your app look and feel more like the first party experiences as well.
Many apps add page transition animations to add style to in-app navigations. While this is a great practice to make your apps more dynamic, excessive use of transition animations can delay load times, particularly when the generation/execution of the animations result in spikes in memory/CPU usage. Balancing the tradeoff between style and performance is a decision that should be made per app. If transition animations are causing performance problems in your app, disabling them completely, or at least on low-memory devices only, can significantly improve in-app navigation performance.
By following these basic principles, you can ensure that your users will have a great experience with your Windows Phone app regardless of the target market. Not only will these optimizations result in great performance on lower cost phones, but the optimizations you make will improve the performance of your app on today’s generation of devices as well, so get a head start tuning your app with the devices you have today. Additionally leverage the 256MB emulator to test your app with the runtime behavior of a 256MB device. Most importantly, have fun building for Windows Phone. We look forward to seeing the great experiences you can bring to the Windows Phone Marketplace.
Make sure to check out the other stories in this series:
- Optimizing Apps for Lower Cost Devices (part 1)
- Optimize Start Up Time (part 2)
- Reduce Memory Usage (part 3)
- Handle Feature Reductions (part 4)
- Respond to User Input (part 5)