1/11/10

WP7 Development Tips Part 1

Performance is the area that we probably spend the most time on in all our apps. Building apps on the phone is just way different than building desktop apps. Things that might be really minor optimizations on a desktop Silverlight can really make a difference on desktop Silverlight apps.

Developing on the phone is an issue of economics where processing power is a scarce resource. You have to be conscious of the tradeoffs of using things like binding, layout controls, dependency injection, etc with their impact on performance. When I first started building phone apps I was excited to use nRoute and it’s nice features around MVVM/Resource Location/Messaging/Navigation. I wanted to have this really perfect loosely coupled architecture that used biding for everything/minimal code behind, had great designer support and dynamically resolved the proper services and models. In practice, that is not generally high performance code on the phone. If you are using some extra frameworks, really be conscious of the impact on performance and decide if you really need that architecture. What might work wonderfully on a more complicated desktop line of business app might not work as well on the phone. You just have to expect to write more optimization in a mobile app regardless if it’s the iphone, wp7 or android.

Silverlight was billed as “same Silverlight, just on the phone”. That is mostly true in terms of the api, but not necessarily true in terms of the actual runtime. It’s really a brand new runtime based on Silverlight 3 with some extra features added, so certain pieces of code might not have the same performance characteristics.

I’ve seen a lot of articles from various other people that talk about “buttery smooth scrolling” and other performance tips. At times the tips are a little too generalized. When you try to optimize something for performance on the phone, you really need to take into account your specific circumstances and find the right combination that works for your app. Always test and benchmark. Some things are more difficult to measure without real profiling tools, but do the best you can. Also be aware that scrolling in 3rd party apps on the phone is just not great at the moment. The native OS apps use a different UI framework that is going to make all but the simplest 3rd party apps feel sluggish so don’t feel too bad if your app scrolling seems slower. It’s probably not entirely your fault. Although this guy (around 8:50) seems to disagree. Sure 3rd party apps will get better with more experience and time, but the runtime needs to also get better. It’s the v1 of a new platform for everyone.

Finally, most of my thought are based around apps like twitter or facebook or other apps that require lots of live network data and have more complicated/longer list based screens. A 2 screen unit converter app is just going to be faster because it’s a simpler app and you don’t really need to optimize much.

So here are some things that you can try think about for your application:

•Data binding is always going to be slower than just directly setting a value. If you don’t have to databind, try to avoid it. I see lots of people going out of their way to MVVM everything and create bindable app bars. Feel free to just wire up a handler once in awhile or just directly set some text. There are other ways of centralizing code for reuse instead of trying to adhere to a strict pattern. If you are trying to animate in a screen and data bind simultaneously, most of the animations will get chopped. Just directly set enough pieces of data to have something to animate in and then you can do more intensive data binding after the animation is complete.
•As mentioned above – consider the tradeoff of always following the same pattern just for the sake of maintaining the pattern. Sure it might be easier to maintain, but high performance code doesn’t always look pretty. Be flexible, take shortcuts and do what makes sense for a specific part of the application. That’s not to say you should ever write bad code, just don’t focus on creating an architectural masterpiece in lieu of something that is performs well. The end user only sees what you put on screen, not the code behind it. They don’t really care that you used MEF and have an awesome messaging framework. When i see people over-engineer what should be a simple app just to adhere to some theoretical best practices I get sad.
•Converters slow your app. It’s better to just create the specific properties needed on your model. It’s minor optimization, but it adds up on the phone.
•Template expansion is slow. If you have a listbox where the item templates have lots of visibility converters, it’s going to be inefficient for a few reasons. First, you pay the perf hit of parsing a template for an item that is just going to be collapsed anyway. Plus you ran it through a bound converter which is slower. To optimize better you can do a few things:
◦If you can cut down visuals in a list, that is always the first place to start. If not every item in the list can use the same fixed template, consider making the list more of a summary and then link to a detail screen. The Twitter app does this nicely to avoid parsing hyperlinks in the text or showing attached photos. (because of how virtualizing stack panel works, it’s always better to have fixed height items that use the same template) Facebook on the hand parses text for hyperlinks which is slower because it need to reassemble the text in a wrap panel of multiple controls and the regexing is slow, but that is more in line with the user’s expectation of the facbeook experience.
◦If you have to have variable templates, make the list item a custom control and have the control dynamically create specific visuals needed for the list item so you don’t do extra layout work. Plus you eliminate converters and other extra data binding since you only have to bind one data property. Creating in code is faster than parsing the extra xaml. Obviously this comes at a maintenance penalty and you don't get designer support, but for those of us that don’t use blend or the designer view, not so much of an issue. Although inside your control you need to keep references to UI elements that you create and reuse as your entire control is recycled by the VSP. Otherwise you lose the benefit of caching that the listbox would normally do. Just make sure you are binding the list to a model that fires propertynotifiedchanged and you can hook that inside your control an update the UI elements as your control recycles.
◦It might seem crazy to not use xaml or databind but in places where we create most of the UI in code we get about 15-20% performance boost.
•Panoramas are slower than pivots because pivots only create the adjacent pivot items, while the pano creates all the items. Panos are nice, but consider the performance implications and don’t use unless there is a good UX reason. A lot of apps use a pano just because it’s there and looks cool but a pivot would probably make more sense and end up being faster.
◦To optimize the pano, defer loading of non-adject pano items. If you have 5 pano items, you only need to initially load the content in the first one and the 2nd one because those are visible on screen (and maybe the last if you want have data there as soon as the user pans backward from the first item) Defer loading of the content in the 3rd one until the user pans to the 2nd one or the 4th one (going in reverse). Same for the 4th one. You could be more extreme and fetch the data for the last one and not bind it until the user actually pans to it since it’s not actually visible when on the first pano item. The overall gains are going to depend on the complexity of the visuals you are loading. Try to keep it simple on the pano.
◦Pivots are more efficient because it only loads adjacent pivot items regardless of the total number of pivots. To improve startup even further, consider collapsing the content inside all but the initial pivot item to avoid even loading the adjacent ones on startup. On loading other pivot items you can uncollapse the content and let it data bind. You will still get the slide in animation. It will be slightly slower to see actual content, but you can overlay a simple loading message so the user sees something. This also improves responsiveness of clicking the pivot headers. We have been using a base page that has a Pivot property so any pages in the app set the pivot property and the base page takes care of uncollapsing the content.
•If you are using a progress bar, make sure you read this http://www.jeff.wilcox.name/2010/08/progressbarperftips2/
•Use HttpWebRequest over WebClient. WebClient always returns on the UI thread. Ideally you want to return on a background thread, do any parsing and then dispatcher begininvoke to bind on the UI thread.
•DataContractJsonSerializer is slower than Json.NET. Switching to Json.Net cut our JSON parsing time in half. That is almost insane. Ideally you want to do this on a background thread as mentioned above.
•If you can make network calls that have both JSON and XML responses, choose JSON. The size of the response is smaller which speeds up network calls and you can use the faster Json.net parser. Also try to return the minimum set of data needed.
•If you are serializing objects to isolated storage, consider implementing binary serialization instead of XML or JSON. It’s more work to maintain and pain to implement if you have complicated object graphs, but the performance difference is tremendous. In one of our apps going from DataContractSerializer to Json.NET to binary improved our iso cache deserialization from 7000ms to 3000ms to 300ms. If you want that data on startup it’s pretty much a necessity,
•Image downloading / decoding is basically kryptonite to a listbox. Binding a list of 100 items with thumbnail or other pictures will make your listbox fairly unresponsive and scrolling performance will be borderline unusable. Right now in the framework all image downloading is on the UI thread and decoding does not seem to be optimized as much as it could be. David Anson’s LowProfileImageLoader code is a must implement for this scenario. That will download all the images on a background thread and offloads everything that does not need to be done on the UI thread. One issue is that the Image control normally caches after it downloads. We have been adding extra code to keep a memory cache of the bitmap sources. Just make sure you put some cap on the size of the cache so it’s not using too much of your 90MB cap. We use 4MB. It' helps to limit the sizes of images that are memory cached. You can check the pixel width / height and only mem cache images under something like 200x200 . You can also cache images to iso storage after you download them. Maybe use that for larger images or ones that will not change over the course of a day. Like if you have a news reader app, there is no sense downloading the images for your top 10 stories every time you launch the app in a day. You will have to have some extra maintenance code so balance how frequently you need to fetch the same images vs the overhead of maintaining a disk cache. At a minimum you can cache large image for the lifetime of your app and just clear the directory on closing. Another optimization you can make is to hook scrolling events and pause the background download thread when scrolling a list. This helps scroll performance. If you are queue up images you might need to reverse the queue when you stop scrolling so the most recently added images load first.
•Try to use jpgs over pngs. The jpg decoder is much faster. Also try to avoid stretching images – if you control the image source, resize them before using in your app – you really don’t want the phone to do any more than it has to.
•When referencing images paths always use /Directory/filename If you omit the leading slash then the framework will do extra lookups
•For certain listboxes evaluate the difference between virtualizing and non-virtualizing stack panels. If you have a huge list you obviously need to use a virtualizing stack panel. Stackpanels have a slower startup time, but scrolling perf is goign to be better. Virtualizing stack panels create about 3 screens of data, but work best when it can fully recycle containers. If you have varying sized items and different containers you lose the benefits of virtualization and have worse scrolling. David Anson has some good code around using a stack panel instead of a virtualizing stack panel that gives you the best of both so see if that might be right for your app.
•If you use the tilt effect, make sure you turn on redraw regions and cache visualization to ensure it’s not adversely impacting your app. The tilt effect seems to invalidate the layout and slow down scrolling performance.
•Also be wary of context menu and the impact on scrolling perf. The runtime has to create the context menu control and attach all the bindings. Not that you shouldn’t use it, but just be aware that there are some performance implications.
•Layout is expensive. Make sure you use the simplest set of xaml to create the desired layout. Try not to nest lots of panels. If you can use canvases as much as possible and fix sized items. Help the runtime out, set a height / width if you can. Otherwise try to keep it to a single gird with multiple rows / columns. Nesting panels requires the layout engine to do more layout passes to measure. Especially on leaf nodes like items in a list box it’s best if you can use a fixed sized canvas. Extra/less efficient xaml that normally wouldn’t make a difference on the desktop has a much larger impact on the phone.
•Microsoft wants apps to run at 60fps on the compositor thread. That is a good goal but it is hard to achieve when a) the UI thread is easily bogged down with layout / binding which hurts the compositor thread perf and b) I’m not sure all the LG phones are even capable of 60fps. Realistically try to keep it as far above 30 as you can.
•Some people say to only use dispatcher.begininvoke when you must. I think there are times when you can use it to queue up visual changes that give the app a smoother appearance.
•Make sure your animations run on the GPU– only use transforms and opacity. If you add other animations in there it will force your storyboard off the compositor thread.
•Content in a popup is not hardware accelerated. If you try to animate stuff in a popup, it will be slow. If you need to mimic popup like functionality, consider re-templating the PhoneApplicationFrame to wrap the contentpresenter in a grid and place a contentpresenter below it to act as a placeholder for a fake popup. You can then write a popup manager to insert whatever child you previously had in a popup into the content presenter/fake popup. This allows you to have better perf on the content in the popup and you can have smoother animations for sliding / flipping the popup into place.
•For binding lists make sure you use observable collections. If you are refreshing a list make sure you merge in changes to minimize the amount of binding updates that occur. It’s more efficient to specifically update individual properties / items that have changed. In you model you can make sure propertynotified changed only occurs when the property has an updated value.
•Since updating large parts of the layout makes the UI somewhat unresponsive, try batching changes. For example if you bind a list to an observable collection, fetch data on a background thread, dispatcher 2 items into the collection, sleep for a few milliseconds and repeat. This will give the UI thread some time to better respond to input. The effect varies depending on your list. Sometimes it just further spreads out the layout cots and it’s worse. You really need to test on a device and determine what feels better. This code is a good example.
•Read the metro UI guidelines. WP7 has a refreshing distinct style. Let’s avoid bad iPhone ports aka the Android marketplace
•Always watch the memory counters. If you get close to 90MB you will have issues. At that point start removing any extra items you may be storing in memory.
•Design an application that requires the user to click the back button. If have a pano as your home screen, don't add buttons on other screens to jump back to that home screen unless you are going to programmatically call goback() until you get to that screen. (you could show some splash screen and do this, but i wouldn’t recommend the UX) The issue is that if you always forward navigate you will never clear the back stack and your app will run out of memory because the previous pages stick around. If you start navigating between panos with lots of textures your app will die quickly. Peter Torr has a post around determining what should be a new page which might cut down on page navigations an improve performance. Quick Rant: I really dislike the back button model most of the time. I think it imposes a navigation structure that doesn’t always make sense. If you have a tree structure like navigation, you can’t easily jump between leaf nodes and back to the root. There really needs to be a way to clear the back stack programmatically. Even web browsers allow you to return to any previous point in the back stack by long pressing the back button. The wp7 model forces you to click back through each page which is annoying in my opinion. Oh and the framework needs the ability to programmatically exit an app. Lots of apps require separate EULAs to use. Why can’t it exit if you don’t accept it?
•I know it’s still a little difficult to acquire a phone, but you have to test on a device. The performance characteristics are enormously different. Almost everything is WAY faster in the emulator.
•Always run through your app and check the fill rate. Try to keep it under 2.5. At 3.5 it will run like junk. If you fill rate is high, use this helper by Dave Relyea (I think it was from him) to dump out the visual tree and see what is contributing to the fill rate. You just need to call TreeHelper.Dump() and it will output the visual tree to the console window along with it’s texture size. You can import this into Excel and sort to find any items that might be contributing unnecessarily to fill rate.
•If you are using system themes you can further optimize the fill rate – just make sure you don’t paint a background at all. That will drop your fill rate by 1. If you want an app to always be black or white, you can put something on app startup to detect the system theme like:
public static Theme GetTheme(this Application application) { var visibility = (Visibility)Application.Current.Resources[PhoneLightThemeVisibility]; return (visibility == Visibility.Visible) ? Theme.Light : Theme.Dark; }Then if the theme is white and you want your app to be white, you don’t have to paint a background and vice versa. To do this I have been painting the background on the root frame. This has the added benefit of not displaying the OS background if you are turnstiling pages in/out. Silverlight should really let you tell the OS what background to paint for your app. It has to paint something. It might as well paint the background color you want.

•Set the app bar opacity to .99 to avoid seeing the OS background bleed through when the app bar animates on/off the screen. Since an app bar with opacity of 1 bumps up the client area, you will see the OS background when the app bar animates away on page transitions. If you do this, you will need to add an prevent the layout root from extending into the app bar space or add a spacer element at the bottom. (Otherwise your content will go under the app bar and you will see it through the opacity. plus it will prevent the bottom most content from scrolling onto the screen. Might seem like a hassle, but I think it looks terrible when an app bar on a dark theme phone animates away and you see this giant black bar for a second.
•Lots of people say don’t use maps in panos/pivots. I think it’s fine as long as they aren’t enabled else the gesture interaction is weird. One issue is that the map is drawn differently than other Silverlight controls so it will render over other content if you have it in a scrollviewer. In that case you can let the map render and on the mapresolved event, take a writable bitmap of the map, remove the map from the visual tree and replace with the image of the map.
•The map control that shipped in the release SDK crashes all the time. Definitely update to one released a few days ago. I don’t know how anyone used it before.
•The web browser launcher tombstones your app and really ruins the UX of your app if you just want to view a quick web page. Consider overlaying in a web browser control and adding and app bar button to open in IE. That or make a setting to either open links in IE or in the app. Or use the long press gesture on a hyperlink to open in IE otherwise open in the app.
•If you want to opt out of system theming, make sure you look at the resources in %ProgramFiles%\Microsoft SDKs\Windows Phone\v7.0\Design\ to clone the font styling and templates to match the default styles but with your own colors.
•The default template for the pivot control doesn’t match the OS style. It should be PhoneFontSizeMedium, PhoneFontFamilySemiBold
•In most of our network calls we have two callbacks, one for cached data and one for live data. Our data services maintain a dictionary cache of the results/ request path, return the cached result, get the new data, update the cache and then return the new results. We then merge the the refreshed callback into the observablecllection with the cached data. This allows screens to load faster on subsequent calls.
•On navigating back to page, you don’t have to rebind / reload data unless you did something on the previous page that requires an update.
•Set all images in your app to be content instead of resources so they are not loaded at startup. This will improve startup performance.
•Regardless of what anyone says, “buttery smooth” scrolling like the native apps is nearly impossible to achieve in silverlight at the moment. If you make a listbox of 200 hard coded textblocks and flick up and down quickly, the screen will just flash and not render content. Either virtualizing stack panel has some issues buffering or the garbage collector is kicking in and causing issues. I’m sure it will get fixed soon, but it’s an issue to be aware of. The only real way to fix the problem is to only have short lists with minimal templates and ideally no images.
•Textblocks with opacity = 0 causes performance problems. You really should avoid that in listboxes.
•If you use the GPS, create a singleton class to instantiate the geowatcher and keep it around. Rather than disposing, just turn it on/off.
•If you have really long lists, consider virtualizing the data. Shawn Oster has a good post on that. If you look at the marketplace on the phone it downloads about 20 items and then downloads more when you scroll to the bottom of the list. This will make the list load faster initially and cuts down on downloading / rendering data that might not be needed. Another option is to add a load more button at the bottom of the list.
•It’s really important to minimize work on the UI thread as much as possible since the touch recognition happens on the UI thread and will get dropped if the processor is maxed out. Don’t do anything on the UI thread that is not directly related to setting / manipulating a UI element. Well you can do some things, but really try to background as many chunks of work as you can.
•Some apps like the people hub scroll the header with the list and have a footer. If you try to retemplate listbox and insert a header and footer around the itemscontrol inside the scrollviewer, you will lose virtualization. There are a few ways you can accomplish this while maintaining virtualization.
◦Easiest is to bind to a list/collection of object and insert dummy items for the header and footer. You can then inherit from listbox and make you own that has a template property for header and footer. In PrepareContainerForItemOverride you can check if it’s a header / footer object and apply the right template.
◦If you don’t want to create all these collections with dummy objects, you could internalize the collection to you own listbox. Instead of binding your normal collection to itemssource, add a new proprty called datasource, bind to that and hook into the collection changed events to mirror the changes in your own internal collection. Then you can bind that internal collection and add your own header / footer items to the collection. This also allows you to easily add some “Loading data” message or “No data” message.
◦Talking to some other people, I have learned that this might not be very performant because it breaks some of the container recycling. The alternative is to not override PrepareContainerForItemOverride and instead have one template and change the visibility on the non header/footer items. This is one of those things that you have to try out and see for yourself.
•Focus in making the app respond to input as soon as possible. You need to do something immediately when the user takes some action. If the content will take some time to load, at least transition to it and display a loading indicator. An example would be a popup that slides in with a list of items. If the list is somewhat complex and does not render immediately, there ill be a noticeable lag between the user action and the response. Slide up the list with a header and loading indicator, then data bind the list. It will feel more responsive.
•I wrote previously about transitions. You should design the app with transitions in mind and be conscious of why certain animations are useful for context and perceived performance. It does seem like this should be easier to do since animation is a big part of the metro design.
•Spend some time to review the hit targets of your elements. 40x40 seems to accommodate an adult finger reasonable well. Remember to keep a reasonable mount of spacing between hit areas. You really need to think about this when designing an application. With a mouse you can click on a specific point. On the phone you need to account for the size of your finger. You want to avoid having users click the wrong things, navigating to a screen , then having to navigate back.
•If you have a pivot with 4 items and you save the selected index on tombstoning you can’t always set the selected index immediately in OnNavigatedTo. The pivot creates the first, 2nd and last items. Setting the selected index to the 3rd pivot will throw an exception. If you have more than 3 pivots and you need to set the selected index, grab the index of of the page state in onnavigatedto, store in a variable and set it in the page loaded event.
•You can always check PhoneApplicationService.Current.StartupMode == Activated to check whether you are returning from a tombstone. The whole concept of tombstoning could be my least favorite #wp7dev feature besides the navigation framework and scrolling performance. I really don’t understand why this is something developers should have to worry about. If the phone wants to tombstone my app, put it back the way it was when you’re done. Also be aware when launchers/choosers are going to tombstone your app or any other side effects when returning from them.
•There are lots of different keyboard layouts (SIP). Use the right one for your text entry needs. You can optimize for web addresses, email, phone numbers, text (for dictionary suggestions) and search. One thing is that the search key is not super obvious in my opinion. You can use the app bar to augment the SIP by adding more specific buttons like upload photo, etc.
•There is currently no socket support so implementing something like a chat client is difficult. *cough* facebok chat *cough *
•If you want your pages to load quickly, don’t do anything before the first layout updated event after loaded is fired. Then defer loading things until they need to be shown.
•There is no launcher for the bing maps app. If you want to display driving directions you need to call the virtual earth web service to get the data (or whatever other map service you might prefer)
•There is also no compass api, no direct camera access (so therefore no augmented reality unless you are a handset maker like LG and have native code access), no Bluetooth api, no ability to add events to the system calendar, no video chooser (thus no ability to upload videos in your app), no ability to customize alert dialogs to match your app if you opt out of theming, no ability to run in the background (you can have your app run under the lock screen though), no copy paste (coming soon and hopefully 3rd party app support), no ability to alter the back stack, no gif decoding support (try ImageTools if you need to display gifs). I’m probably missing a few things that i would like to use but are not not supported. Despite all that, I’d still rather use visual studio / c# than Xcode/obj-c I do think the silverlight team did a great job getting it on the phone. It really is one of the better frameworks for building UX centric apps and the silverlight team has been really helpful in tracking down issues we’ve encountered.
I’ll be at PDC if anyone would like to chat about #wp7dev Sphere: Related Content

2 comentarios:

Anónimo dijo...

Αѕ the admіn of thiѕ sіte
іs woгking, no dоubt νery quicklу it will be renowned, due to its qualitу contents.


my weblog :: lloyd irvin

Anónimo dijo...

Hey! Ι juѕt wanteԁ to ask if yоu evеr haνe any problems with hacκerѕ?
Mу last blоg (ωorԁpress) was hacked and Ι enԁed uρ losing sеveral wеeks οf hаrԁ worκ duе to no back
uρ. Dо you have аnу mеthoԁs to stoр hackers?


my website: reputation management