Like all mobile devices, Windows Phone 7 devices have limited processing, memory, and storage facilities compared to a desktop or laptop computer. It is important to take this into account when building applications that will run on Windows Phone 7.
The main areas of concern when designing mobile device applications are to manage device resource usage effectively and maximize performance on the device. Typically, maximizing performance involves optimizing memory utilization, maximizing use of the graphics processing unit (GPU), and more general issues, such as good programming practice. The following sections provide guidance on achieving these aims in your applications.
Minimize Device Resource Usage
reduce battery life. You should consider limiting use of these features when not necessary. For example, you should access the location service (which may initiate the built-in GPS) only when necessary, and turn it off when not required. Consider implementing options or power profiles in your applications that allow the user to minimize power use or turn off features of the device when not required.
Also consider how you can minimize the use of the network, Bluetooth, storage, video, sound, and background tasks. All of these require processor and memory resources, and they impose an additional load on the battery. For information about using the device capabilities, such as location services, and minimizing their impact on performance, see Appendix C, “Leveraging Device Capabilities.”
Apply Good Practice Programming Techniques
You must be aware of the work your application is doing and the use of resources within your code. Where possible, you should attempt to do the following:
- Apply the optimizations you would use in any other .NET Framework code, such as caching the appropriate types and volumes of data, avoiding boxing (which occurs when you treat a value type as an object), and using lazy loading so that memory is only allocated to objects if they are actually used by the application.
- Use asynchronous programming techniques to perform any complex processing tasks on background threads instead of on the UI thread. Use Dispatcher.BeginInvoke to initiate operations on the UI thread, and pass data between tasks on different threads using local variables, a memory buffer, or isolated storage. Also consider using the Reactive Extensions (Rx) to implement asynchronous execution for tasks. Rx can simplify asynchronous programming tasks and make code easier to read and assimilate.
- Minimize the visual tree of objects and code execution. Less activity in the application will give better performance and reduce battery drain.
- Minimize the size of objects that you redraw or animate within the UI and the number of operations (such as transitions or effects) you perform on the objects. If you use code to drive animations with per-frame callbacks, these execute on the UI thread and may appear less smooth than those on the compositor thread when the UI thread is busy.
- Ensure that media you display or play (such as video or audio) is encoded at the appropriate size for display, and that it has the minimum acceptable frame or audio bit rate.
- Minimize the processing that occurs in the Loaded event of the initial page, which will delay initial display of the application. Alternatively, consider using the splash screen (SplashScreen Image.jpg) that is included in each Windows Phone 7 project template. You can modify it to show a suitable image while the application loads.
- Test regularly on a physical device, not just by using the emulator, because the performance of the two will vary dramatically based on the hardware available to each.
Optimize Memory Usage
running on Windows Phone 7 if you carefully control use of memory and isolated storage. You must also be aware that garbage collection in Windows Phone 7 devices works differently than it does in desktop and server versions of the .NET Framework. In Windows Phone 7, the garbage collector runs only when memory usage exceeds a specified level, and it can interrupt execution—which can, therefore, affect performance.
Where possible, you should attempt to do the following:
- Optimize data downloads and store only data that is actually required on the device.
- When accessing large sets of remote data, consider using data paging so that only the data that will be viewed is sent to the device.
- If you will reuse objects you create in your code, consider holding them in memory instead of allowing them to be garbage collected and then recreating them. This reduces the frequency of garbage collection, which can interrupt program execution to a greater extent than in a desktop application. However, do not hold onto objects that will not be regularly reused, and consider minimizing the total number of object instances that you create in order to reduce memory requirements. Using a few large objects instead of a large number of small objects can also reduce garbage collection overhead, but you must also consider whether the performance gain outweighs the benefits of
smaller, more-focused and decoupled objects that follow the single responsibility principle.
- Consider creating or loading the objects and data you need when your application starts. If you minimize the requirement to allocate memory for new objects as the application runs, the garbage collector will run less often. If you use mainly value types and arrays of value types, you will minimize the time that the garbage collector takes to execute. However, you must balance the effects, depending on whether you require fast startup or faster execution after startup. Alternatively, consider downloading the data you need asynchronously on a background
thread and allowing the application to start with some limit to its functionality and then gradually enable features as the data that they need becomes available. A dependency injection container can help you to manage object creation.
- Consider calling the GC.Collect method to force garbage collection when the application is at a point where there is no interaction with the user and no major tasks are running in your application. This may be immediately after initializing when the main screen is displayed or at a suitable point where the user is reading content you have displayed. This reduces the chance that it will run at a more intrusive time and affect the interactivity of your application.
- Consider simplifying the structure of your application components when using design patterns that provide decoupling (such as MVVM or Dependency Injection) if you can do so without fundamentally affecting the architecture or objectives of the patterns. For example, you may be able to share a view model between two or more views.
- Minimize the size of the application assemblies. Use a Build Action of Content (instead of Resource) for media, images, and other content to include them as files in the XAP file instead of as resources embedded within the assembly. However, if you are building a reusable component, you will probably need to embed the resources within the assembly for easier distribution.
Note: You can query the current memory availability and usage by your application using the DeviceExtendedProperties class in Windows Phone 7. For more information, see Appendix C of this book, “Leveraging Device Capabilities,” and “Device Information for Windows Phone” on MSDN (http://msdn.microsoft.com/en-us/library/ff941122(VS.92).aspx).
Maximize GPU Usage
Windows Phone 7 provides a background compositor thread that generates output for display and a relatively powerful GPU that maximizes performance for graphics and maintains an acceptable frame rate. However, you must take care to not negate this advantage through the use of code that cannot run on the GPU.
Where possible, you should attempt to do the following:
- Minimize the code you execute on the UI thread, which handles input, the drawing of new visual elements, and calls to user code. Use the compositor thread to execute transitions and animations, and apply caching for bitmaps. The compositor thread handles opacity, scale transforms, translate transforms, rotate transforms, and plane projection. It does not handle opacity masks, non-rectangular clips, and texture for animated objects greater than 2000 × 2000 pixels.
- Consider caching complex visual elements if they will be regularly reused. When you hide a visual element (visibility = collapsed) it is discarded and requires no processor utilization. This means that it must be completely recreated when you show that element again. Instead, consider setting the opacity to zero. This causes the device to keep the object, while still minimizing processor usage. However, you must balance this with memory usage.
- Consider using images instead of creating complex visual elements in Extensible Application Markup Language (XAML) or code. This can considerably reduce processor load. If you need transparency, you must use a PNG image. If you do not need transparency, use a JPEG image because the render speed is higher for this image format.
- Images larger than 2000 × 2000 pixels will be sampled at a lower resolution when displayed, with subsequent loss of quality. Store larger images in a WriteableBitmap and display only the appropriate section.
There are several modes you can enable when debugging. These are especially useful when maximizing graphics performance; they are automatically included in the code generated by the phone application template in Visual Studio. Graphics debug mode is set by the following properties of the Application.Current.Host.Settings class in the startup code in your App.xaml.cs file:
- EnableFrameRateCounter. This displays a frame rate counter, memory usage information, and other useful data about the performance of the device. It shows the frame rates for the UI and the compositor threads, the amount of texture memory in use, the number of surfaces, and the fill rate. Ensure that the SystemTray.IsVisible static property is set to false in your application so that the system tray does not obscure the frame rate counter.
- EnableCacheVisualization. This shows the graphics items that are being drawn by applying tint and transparency to each one to show where overlapping textures are being drawn on each frame update. Each tinted area represents a texture that is handed off to the GPU for composition.
- EnableRedrawRegions. This shows the graphical items that are being redrawn in each frame update.
For more detailed information about the background compositor thread and maximizing performance on Windows Phone 7, see Creating High Performing Silverlight Applications for Windows Phone from the Microsoft Download Center (http://download.microsoft.com/download/B/2/7/B2748D7A-F368-4C33-B0F2-844CFE085193/SilverlightForWindowsPhonePerformance. zip). For more background information about performance optimization, see the video presentation, Silverlight Performance on Windows Phone, on the Mix 10 website (http://live.visitmix.com/MIX10/Sessions/CL60) and “WP7/SilverlightGraphics Performance” on andybeaulieu.com (http://www.andybeaulieu.
These and all links in this book are accessible from the book’s online bibliography. The URL to the bibliography can be found in the preface, in the final section, entitled, “Where to Go for More Information.”