I co-authored this article with Alex Golesh, of Silverlight MVP fame.
Windows Phone SDK 7.1.1 update is a minor upgrade with two primary goals: to make it easier for developers to reach more consumers and bug fixes (>500 bugs fixed). To make Windows Phone devices easier to acquire and distribute, Microsoft has permitted device manufacturers to build lower-cost devices. These devices have only 256 MB of memory and weaker CPUs. They may also have other physical limitations. For example, some devices may not have a camera. Making those devices available is vital to helping you reach more potential customers because their lower cost makes them available to more people. In addition, Microsoft added 23 more supported countries to Windows Phone Marketplace.
The speed and responsiveness of the Windows Phone OS has been one of its hallmarks since it first hit the market. Microsoft has always seen performance as critical to ensuring a high-quality user experience. To ensure newly supported devices do not suffer performance degradation due to limited memory and CPU resources, Microsoft has imposed some limitations on these devices.
Supporting low-memory devices may require you to make some changes to your code, however, the development effort required to add support for these new devices are minimized as much as possible. Many apps written for Windows Phone 7.1 will run on Windows Phone SDK 7.1.1. update, and vice versa, with no effort whatsoever. But if your app requires more memory than is available on the low-memory devices, you can choose to make it available only to high-end devices, or you can optimize your app for both types of devices.
Note: This is a pre-release version and does not yet have a “GO LIVE” license. You are not allowed to publish Windows Phone applications to the Marketplace using this update.
Manifest Declaration Changes
If you test your app and determine that it is not supported on low-memory devices, you should also modify your app’s manifest file and specify that it requires a high-end device. This is an explicit way to tell the Windows Phone Marketplace that the app should not be permitted to run on low-memory devices.
To specify that your app is only supported on high-end devices, add a Requirement element to the new Requirements section of the WMAppManifest.xml file specifying the ID_REQ_MEMORY_90 requirement.
<Capabilities> ... </Capabilities> <Requirements> <Requirement Name="ID_REQ_MEMORY_90" /> </Requirements>
Note: The Requirements element must appear immediately after the Capabilities element. Apps without the ID_REQ_MEMORY_90 requirement declaration will be visible to all devices, low-memory and high-end.
Although enabling support for low-memory devices makes your app available to more users, you should test your app to ensure it works well on devices with 256 MB RAM. Apps that are not optimized for the constrained environment may run out of memory or perform perceptibly slower. This can be detrimental to the user experience and may lead to poor reviews in the Marketplace. If you’re not sure whether your app runs well on a low-memory device, you should probably disable support until you have an opportunity to test and optimize your app.
To test you application behavior on low-memory device, you can use the updated performance tools provided with the Windows Phone Developer Tools update for Windows Phone Tango to emulate a low-memory environment and to analyze your app’s memory consumption. We’ll expand on this shortly.
Targeting Low-Memory Devices
Low-memory devices use lower-cost hardware components that impose some physical limitations you should be aware of.
Windows Phone never allocates all the RAM to your app. On a high-end device, the OS allocates at least 90 MB RAM. On low-memory devices, it usually allocates a lot less. To determine whether the device is a low-memory device, you simply have to check whether the OS is allocating less than 90 MB. The following example shows one way to retrieve this information and make it globally available to your app.
using Microsoft.Phone.Info; //... public partial class App : Application { public static bool IsLowMemoryDevice { get; private set; } public PhoneApplicationFrame RootFrame { get; private set; } //... private void Application_Launching(object sender, LaunchingEventArgs e) { try { var workingSetLimit = Convert.ToInt64(DeviceExtendedProperties.GetValue("ApplicationWorkingSetLimit")); IsLowMemoryDevice = workingSetLimit < 94371840; // 90 * 1024 * 1024 } catch(ArgumentOutOfRangeException) { //API is not supported - Mango device IsLowMemoryDevice = false; } } }
The code asks the DeviceExtendedProperties class for the new value of the “ApplicationWorkingSetLimit” extended property (which is supported starting from Tango). This extended property determines how much memory (measured in bytes) is available to your app while it’s active. If your app tries to use more memory than the working set limit, your app will crash with an OutOfMemoryException.
This extended property was introduced in the Windows Phone Tango patch. When you run this code on Windows Phone 7.0 and 7.1, they will not recognize the property and will throw an ArgumentOfRangeException. All the devices manufactured and distributed worldwide before the Windows Phone Tango patch are high-end devices. So if this exception occurs, you can safely assume that your app is running on a high-end device.
Devices running Windows Phone Tango will recognize the property and return the available memory. Because they might be either low-memory or standard devices, we still have to check the size of the memory allocation. If the limit is less than 94371840 bytes (90 MB), the device is a low-memory device.
To optimize the app for low memory conditions, simply call the IsLowMemoryDevice property when you need to choose whether to use an optimization:
if(App.IsLowMemoryDevice) { // low-memory device optimizations //... } else { // normal memory conditions //... }
If you use MVVM or a similar design pattern, you should consider adding this property to a view model or a globally available service instead of the App class. For more info, please refer to MSDN documentation: http://msdn.microsoft.com/en-us/library/hh855083(v=vs.92).aspx
Background Agents
Windows Phone Tango on low-memory devices does not support periodic and resource-intensive tasks. Support for these background agents was removed to prevent the limited memory from detracting from the user experience.
If your app uses a PeriodicTask or a ResourceIntensiveTask for notifications, to update a Live Tile, or for other purposes, you have two options:
- You can disable support for low-memory devices in the app manifest and when you submit the app to the Marketplace.
- Or you can add code to your app that checks whether the device supports background agents, and launch them only on devices that support them.
The following code example demonstrates how to launch background agents selectively. It also accounts for situations where background agents cannot be launched for other reasons.
if(App.IsLowMemoryDevice) { // do not launch general background agents //... } else { try { var periodicTask = new PeriodicTask("SomePeriodicTask"); ScheduledActionService.Add(periodicTask); } catch(InvalidOperationException) { // there are too many background agents or they are disabled } }
If you try to schedule a PeriodicTask or a ResourceIntensiveTask on a low-memory device, Windows Phone Tango will throw a SchedulerServiceException. If your app absolutely relies on these agents, do not declare support for low-memory devices when you submit your app to the Marketplace.
Best Practices
Best Practices – Silverlight apps
When you optimize your app for low-memory devices, always keep in mind that less memory is available. In general, any task that requires significant memory or intensive processing should be tested and optimized in a 256 MB environment. The following list provides guidelines on how to avoid common pitfalls. In many cases, these guidelines will improve the performance on high-end devices as well.
- Avoid long lists. Long lists take a lot of memory. Users usually do not need to see a full list of data. Use filtering and/or paging to optimize long lists. For example, instead of listing 1,000 items all at once, split the list into pages and show only 20 items on a page. If you only retrieve 20 items at a time from your data source, you can dramatically reduce the memory consumption of your application.
- Use data binding with lightweight objects. When you bind the UI to objects that have a lot of data, Silverlight has to keep all the data in memory even though you might only display one or two properties. A better option is to create light weight objects intended explicitly for binding with the UI, such as view models that only have properties that are displayed. When you retrieve complex data, you can create these lightweight objects and throw away the complex data objects, thereby reducing the overall memory footprint. Using view models with the MVVM design pattern can provide additional benefits as well, such as a loosely coupled UI and better application architecture.
- Minimize use of the WebBrowser and Bing Maps Silverlight controls on low-memory devices. If you use these controls in your app, make sure to test their use in a low-memory environment. These controls manage a lot of data, and if your app also works with a lot of data, you might reach the memory limit quickly. The best approach is to create, initialize and load the controls only when you really need them, and dispose of them as soon as you don’t need them anymore.
- Use a smaller cache. Caching is very important, especially for connected applications. However, caching is a memory-intensive operation. You should use caching when appropriate, but limit the size of the cache. For example, if your app uses a cache that stores 200 items, you might want to consider limiting it to 50 items on low-memory devices. If you cache a few pages of results in a list that uses paging, consider caching just one or two pages instead. A smart caching strategy can improve the perceptible performance of your app even on low-memory devices.
Best Practices – XNA apps
To optimize sound effects usage:
- Provide a lower sampling rate for sound effects. Higher sampling rates (48KHz vs. 22KHz) and higher bit-depth (8 bit vs. 16 bit) sounds consume more memory. Not all sound effects in a game may require high sampling rates.
- Dispose the old SoundEffect instances when you no longer need them.
- Use fewer sound effects. In some cases, you can use a single sound to represent all enemies rather than an individual enemy sounds.
To optimize graphics usage:
- Compress textures
- More about that in Shawn Hargreaves’ blog post: http://blogs.msdn.com/b/shawnhar/archive/2008/10/28/texture-compression.aspx
- Compress vertex data
- More about that in Shawn Hargreaves’ blog post: http://blogs.msdn.com/b/shawnhar/archive/2010/11/19/compressed-vertex-data.aspx
Tooling
To get started with the patch, download and install the Windows Phone SDK Update CTP. The patch is a minor update to the 7.1 version of the tools and can safely be installed over the previous version.
The Windows Phone SDK Update CTP release makes the transition from Windows Phone 7.1 as easy as possible for developers. Apps written for Windows Phone 7.1 will run on Windows Phone Tango, in most cases, without any changes. Also, because the Windows Phone SDK Update CTP is a minor update, most of the tools should be familiar to programmers with any Windows Phone development experience.
In fact, when you create a new Windows Phone application, Visual Studio shows you the New Windows Phone Application dialog that lets you choose the target OS for your app. This is exactly the same dialog as the one introduced with the Windows Phone 7.1 tools, and the target OS options are identical: Windows Phone OS 7.0; and Windows Phone OS 7.1, which now includes Windows Phone SDK 7.1.1 update.
The most significant area where you might have to invest some effort is in testing and profiling your app to ensure it runs well on low-memory devices. You do not have to purchase a low-memory device to test your app. Microsoft has added and improved a couple of tools to help you do this effectively.
The Emulator
The first difference you’ll probably notice after installing the Windows Phone Developer Tools update is that the Target Device dropdown next to the Run button on the Standard toolbar has changed a bit. The “Windows Phone Emulator” option has been renamed to “Windows Phone Emulator – 512 MB” and a new option “Windows Phone Emulator – 256 MB” has been added.
When you test your app on the 256 MB emulator, it mimics the memory constraints of a low-memory device. Run your app and play around with it a bit on this emulator to get a feel for how your app behaves in this environment. If it feels sluggish or freezes often, or if it crashes, it means your app needs more memory than low-memory devices can allocate. You’ll have to invest some effort in optimizing it for low-memory devices.
You can also run both emulators (the 256 MB and 512 MB emulators) side by side. Running both emulators at the same time can help you test apps that have divergent code for low-memory and standard devices by providing quick feedback for both environments.
DebugInfo
To ease on debugging on different device types, my colleague Alex Golesh has created some reusable libraries for Silverlight, XNA, and Silverlight/XNA hybrid applications. Those libraries provide easy integration and visible cues for each device type on used memory and memory constraints.
Integrating the libraries with your Silverlight app is as easy as adding a number of lines of code:
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e) { MemoryInfo mi = new MemoryInfo(); mi.UpdateInterval = 1; mi.IsEnabled = true; base.OnNavigatedTo(e); }
This is what it looks like in a Silverlight app:
To show the info on an XNA page in your hybrid app, use the UIElementRenderer:
DebugInfo.Hybrid.MemoryInfo mi = new DebugInfo.Hybrid.MemoryInfo(); mi.IsEnabled = true; LayoutRoot.Children.Add(mi);
Note: Alex has written more about the UIElementRenderer in an older blog post.
This is what it looks like in an XNA page in a hybrid app:
To integrate the libraries in an XNA game, use the XNA’s DrawableGameComponent like you usually would:
MemoryInfo mi; //... mi = new MemoryInfo(this); Components.Add(mi);
This is what it looks like in an XNA game:
Note: The XNA screenshot was taken using a 512MB emulator. On a 256MB emulator, a “Low memory device” warning appears.
General note: The DebugInfo overlay appears only when using the Debug configuration.
Download the DebugInfo libraries to get started.