This is part three of my Widget Anatomy series which which will explain the ins and outs of the Widget Framework that is shipping as part of Windows Mobile 6.5. In this installment I will discuss how to squeeze the last drop of performance out of the Widget framework to ensure all our Widgets are interactive, alive and fun to use while being good mobile citizens and not draining the user’s battery dry in the process.
All good things
JavaScript is a cool language. It’s simple to learn, use, and understand but it really can get us into a lot of trouble if not used wisely. this is true on all platforms but we must pay special attention to its implications while developing for a mobile platform.
So, just because we are among friends here I’ll share a little secret that showcases the biggest challenge to creating responsive Widgets. Here it goes… are you ready?… On Windows Mobile 6.5, JavaScript execution blocks the UI. This means that, whenever there is a script running nothing else can be processed in the widget which includes menu commands and keyboard or touch input processing. Also note that, during script execution the Widget can’t redraw – in other words, any screen updates done during the execution of the script will not be reflected on the screen until after the script finishes its execution.
I know that this sounds like a big obstacle to overcome but it is really not that bad.
One Small Step
On very interesting aspect of performance is that, in most cases, perception is reality. In a mobile application world, this means that for every user action there is a immediate response from the application that assures the user that their input was acknowledged and is being processed.
The best way to accomplish this on widgets is to minimize the amount of code that is executed on the event handler and defer all actions that are not related to updating the UI to give feedback to the user after a small timeout.
1: function onCoolActionClick() {
2: document.getElementById('waitCursorLayer').style.display = "block";
3: window.setTimeout("doCoolStuff()", 50);
4: }
5:
6: function doCoolStuff() {
7: countToAMillion();
8: document.getElementById('waitCursorLayer').style.display = "none";
9: }
The same technique can be applied to break out expensive operations to allow processing of the always important user events.
1: function countToAMillion(currentCount) {
2: // Count to a million in 10 incremets to ensure the UI events can be processed
3: var countBlockStarts = currentCount;
4: var countBlockEnds = countBlockStarts + 10;
5:
6: for(var i = countBlockStarts; i < countBlockEnds; i++) {
7:
8: // Check to see if we are done
9: if(i >= 1000000) {
10: return;
11: }
12:
13: // Write the current count
14: document.getElementById("currentValue").innerHTML = i;
15: }
16:
17: // schedule the next few elements
18: window.setTimeout("countToAMillion(" + countBlockEnds + ")", 50);
19: }
Another trick that will come handy for most Widgets since they are generally full of dynamic content is that using innerHTML is significantly faster than creating the DOM elements individually and then appending them to the document. You should never append them to the document one by one, as this will completely destroy your performance since it will force the Widget to re-layout multiple times — and this is really slow!. I know that this might not be possible in all cases but it is a nice tool to have in case your widget is having performance issues creating dynamic content.
Awakening
Now let’s focus our attention to the flip side of performance which is, without a doubt, battery life. It is a fact of life that our Widgets, since they are executing code on the device, will have an impact on battery life. The goal is to minimize its effects to ensure happy users.
Thankfully the road to user happiness is not as rough as it might seem, there are a few principals that, if we keep them in mind while writing our Widget code, then we will know for sure that we are only using the power that is necessary to complete our task.
- Only consume network resources when your widget is active.
- Minimize animations when the widget is idle.
- Cache data locally as aggressively as possible, use the widget persistent storage as a repository since it will be preserved across runs (and even upgrades).
- Be aware of the current battery level and minimize network operations when the battery is running low.
- Always (and by that I really mean always!!) use asynchronous network requests.
Example:
1: function onShow() {
2: // The widget was activated and is running in the foreground
3: // refresh the widget data
4: refreshWidgetData();
5: }
6:
7: function onHide() {
8: // Stop al recurring timers to be a good Widget citizen
9: stopAllTimers();
10: }
11:
12: function onLoad() {
13: var systemState = widget.createObject("SystemState");
14: var batteryStrength = systemState.PowerBatteryStrength;
15:
16: batteryStrength.addEventListener("changed", optimizeNetworkUsage);
17:
18: widget.onshow = onShow;
19: widget.onhide = onHide;
20: }
That’s it for now, but before I completely forget, one set of tools that will be your friend tracking performance issues on your widgets (and web applications as well) is the IE8 amazing developer tools (to start them, just press F12) which include a script profiler and it works great for widgets!
Next post: Widget Anatomy – Security