Thursday, October 25, 2012

Promisable Windows Runtime Component

The async methods in Windows Runtime component can be consumed directly by C# apps, but not by JavaScript. In order to expose async method to JavaScript world, simply wrap it using AsAsyncOperation() method:
using System;
using System.Threading.Tasks;
using Windows.Foundation;

namespace WinRTComponent
{
    public sealed class Test
    {
        public IAsyncOperation<double> TestAsync(int a, int b)
        {
            return DivideAsync(a, b).AsAsyncOperation();
        }

        private static async Task<double> DivideAsync(int a, int b)
        {
            if ( b == 0 )
                throw new InvalidOperationException("Dividing by zero!");
            await Task.Delay(1000);
            return 1.0 * a / b;
        }
    }
}
Inside WinJS project you just add reference to the runtime component, and the async method will be availale (intelliSense works as well):
    WinJS.UI.Pages.define("/pages/home/home.html", {
        ready: function (element, options) {

            var tester = new WinRTComponent.Test();
            tester.testAsync(123, 2).then(function (result) {
                console.log(result);        // result = 61.5
            });
            tester.testAsync(123, 0).then(
                function (result) {
                    console.log(result);    // not reachable 
                },
                function (error) {
                    console.log(error);     // InvalidOperationException 
                });
        }
    }
Note that the async method name automatically becomes camel case (TestAsync => testAsync). Also the exception thrown by runtime component will fall to error function handler in WinJS promise then function. The exception type is visible but the original excetion string is invisible from JavaScript side:

Saturday, October 13, 2012

Android Activity Life Cycle

Android apps with UI interaction are composed of one or more Activity. It's crucial to understand Activity life cycle just as ASP.NET developers have to know how the Page object is constructed. Following image from Android developer guide shows the overall flow:

A few notes on events for different user actions:

  1. Hit home/power button: onPause() => onStop(). Stop state means in Activity in background, and it's no longer visible on the screen. From documentation Android OS could kill the activity process (onDestroy() will be called) when memory reaches a low point, but I can't effectively reproduce that in a physical device.
  2. From 1 back to activity: onRestart() => onStart() => onResume(). This is quite straightforward. If the low-memory-kill-background-process happened, a new Activity instance will be recreated from scratch.
  3. Hit back button: onPause() => onStop() => onDestroy(). The previous Activity will show up and current activity is destroyed and gone. The app will close if no previous activity exists in navigation history.
  4. Rotate the screen from landscape to portrait or vise verse: onPause() => onStop() => onDestroy() => onCreate() => onStart() => onResume(). The whole activity is destroyed and recreated. That's very different from Windows 8/Windows phone 8 implementation where that only triggers an event and the instance of the page or class remains. Android considers screen rotation, language change, storage plug/unplug, etc. as "runtime configuration change". The Activity will be reloaded by default when configuration is changed, so a new layout xml file as well as other related resources can be reconstructed properly. You can skip this behavior by overriding onConfigurationChanged method to handle the situation by your own, but such approach is not recommended from Android developer guide.

Another interesting thing is the state management, two scenarios in particular: 1. background Activity is destroyed due to low memory and then is back to active Activty; 2. foreground Activity is destroyed and rebuilt because of the screen rotation. Android provides onSaveInstanceState method for you to save your data, which is executed after the onPause(0 and before the onStop() callback handlers, then you can restore the data in onCreate() method:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
 
        // check if saved state data exists 
        if (savedInstanceState != null) {
            int prevSelectedNumber = savedInstanceState.getInt("SELECTEDNUMBER");
            // do UI logic based on prevSelectedNumber
        }
    }
 
    @Override
    public void onSaveInstanceState(Bundle savedInstanceState) {
        super.onSaveInstanceState(savedInstanceState);
 
        // save instance state data
        savedInstanceState.putInt("SELECTEDNUMBER", 123);
    }

Note that onSaveInstanceState() method is only called when the Activity is destroyed by Android OS internally like the orientation change discussed above, but it will not be called if the Activity is killed by the user action such as turning off the device or pressing the back button to go to previous Activity. In some cases, for example the address/contact input form, it would be great to keep unsaved and uncompleted data to avoid user retyping when the user is going back and forth to different screens, then the data should be saved during onPause() callback.

On top of Activities there're a set of rules defining how an Activity instance is created and behaves in one or multiple applications, e.g. a browser Activity declared as singleTask launch mode will result in only one instance running inside one Task cross the whole Android environment. The details are well described in this Android Developer Guide.

Friday, October 12, 2012

Eclipse Tips

I mainly use Visual Studio for development in the past 8 years. Recently I switch to Eclipse to do some Android work. Not like old days I had to spend a lot of time on memorizing a list of commands to run the vi editor, modern IDEs are quite easy to work with, but the right settings and handy short-cuts can still speed up your work. Below is some of my findings on Eclipse that helps me to do my work more efficiently.

Preference Settings:

1. Show code line number:

I am not sure why both Eclipse and Visual Studio are not showing line number by default. The line number is quite useful for me to pin point the source code, so I turn it on the first place.

2. Save before build but not automatically build.

Unlike Visual Studio Eclipse will not save your changes when you click build or run the application. On the other hand, Eclipse automatically build(compile) the source files in the background every time you make a change in source file. I don't like both default options and here is where you can change the behavior:

3. Enable auto insert:

This is a nice feature in Eclipse which is not available in Visual Studio or I am not aware of. Basically you can type ";" or "{" anywhere during coding and Eclipse will automatically put it to the right spot, the ending of the code line.

4. Enable step filters:

With “Step Filters” you will skip stepping into those types during debugging, so you could just focus on your own code.

Handy short-cuts

Ctrl+Space content assist just like intellisense in Visual Studio
Ctrl+Shift+F format code
Ctrl+/ comment/uncomment code with //
Ctrl+Shift+/ comment code with /**/
Ctrl+D delete line
F3 go to declaration
Ctrl+Shift+O auto import (when this available in Visual Studio?)
Ctrl+T show type hierarchy
Ctrl+Shift+T search for type
Ctrl+Shift+R search for resource
Ctrl+Shift+G find all references
Ctrl+H open advanced search window
Alt+Left/Right navigate source back and forward like Ctrl-/+ in Visual Studio

Saturday, October 06, 2012

WinJS Promise Any and Join

WinJS.Promise.any function will return another Promise object when any of the Promise objects defined in an array completes. The object passed to next Promise function from Promise.Any function has key and value properties. The key property is the array index (zero-based) of the first completed Promise, and the value property is another Promise object which wraps the return of first completed Promise, as demoed in following code snippet:
    function addAsync(number1, number2, delayInSeconds) {
        return new WinJS.Promise(function (c, m, p) {
            setTimeout(function () {
                c(number1 + number2);
            }, delayInSeconds * 1000); 
        });
    }

    function test() {
        var promise1 = addAsync(1, 1, 3);
        var promise2 = addAsync(2, 2, 2);
        var promise3 = addAsync(3, 3, 1);

        WinJS.Promise.any([promise1, promise2, promise3]).then(function (data) {
            var key = data.key;            // key = 2
            var value = data.value;        // value is a Promise
            var realValue = value._value;  // realValue = 6
        });
    }
On the other hand, Promise.join function will wait for all Promises in an array to complete before the then function starts. The same size of array holding each Promise's return will be passed to the then function:
    function addAsync(number1, number2, delayInSeconds) {
        return new WinJS.Promise(function (c, m, p) {
            setTimeout(function () {
                c(number1 + number2);
            }, delayInSeconds * 1000); 
        });
    }

    function test() {
        var promise1 = addAsync(1, 1, 3);
        var promise2 = addAsync(2, 2, 2);
        var promise3 = addAsync(3, 3, 1);

        WinJS.Promise.join([promise1, promise2, promise3]).then(function (data) {
            var promise1Return = data[0];    // promise1Return = 2
            var promise2Return = data[1];    // promise2Return = 4
            var promise3Return = data[2];    // promise3Return = 6
        });
    }
What happens if exception occurs inside the Promise? Let's change a little of the test code:
    function test() {
        var promise1 = addAsync(1, 1, 3);
        var promise2 = addAsync(2, 2, 2).then(function () { throw "promise2 error" });
        var promise3 = addAsync(3, 3, 1);

        WinJS.Promise.any([promise1, promise2, promise3]).done(function () {
            console.log("Promise.any completed");
        }, function (err) {
            console.log("Promise.any err: " + err);
        });

        WinJS.Promise.join([promise1, promise2, promise3]).done(function () {
            console.log("Promise.join completed");
        }, function (err) {
            console.log("Promise.join err: " + err);
        });
    }
The result is:
Promise.any completed
Promise.join err: ,promise2 error
We can see Promise.any function would complete successfully if any of the Promise object is completed without error. But Promise.join would fail and jump to error handler if any Promise object throws exception. There's a comma "," in error message because like the return data in okay case, the errors are wrapped to an array and mapped to the Promise index, in above case: err[0] is undefined and err[1] is "promise2 error" message.

Reference: the source of Promise.any and Promise.join is defined in base.js from WinJS library:
  any: function Promise_any(values) {
        /// <signature helpKeyword="WinJS.Promise.any">
        /// <summary locid="WinJS.Promise.any">
        /// Returns a promise that is fulfilled when one of the input promises
        /// has been fulfilled.
        /// </summary>
        /// <param name="values" type="Array" locid="WinJS.Promise.any_p:values">
        /// An array that contains promise objects or objects whose property
        /// values include promise objects.
        /// </param>
        /// <returns type="WinJS.Promise" locid="WinJS.Promise.any_returnValue">
        /// A promise that on fulfillment yields the value of the input (complete or error).
        /// </returns>
        /// </signature>
        return new Promise(
            function (complete, error, progress) {
                var keys = Object.keys(values);
                var errors = Array.isArray(values) ? [] : {};
                if (keys.length === 0) {
                    complete();
                }
                var canceled = 0;
                keys.forEach(function (key) {
                    Promise.as(values[key]).then(
                        function () { complete({ key: key, value: values[key] }); },
                        function (e) {
                            if (e instanceof Error && e.name === canceledName) {
                                if ((++canceled) === keys.length) {
                                    complete(WinJS.Promise.cancel);
                                }
                                return;
                            }
                            error({ key: key, value: values[key] });
                        }
                    );
                });
            },
            function () {
                var keys = Object.keys(values);
                keys.forEach(function (key) {
                    var promise = Promise.as(values[key]);
                    if (typeof promise.cancel === "function") {
                        promise.cancel();
                    }
                });
            }
        );
    },
  join: function Promise_join(values) {
        /// <signature helpKeyword="WinJS.Promise.join">
        /// <summary locid="WinJS.Promise.join">
        /// Creates a promise that is fulfilled when all the values are fulfilled.
        /// </summary>
        /// <param name="values" type="Object" locid="WinJS.Promise.join_p:values">
        /// An object whose fields contain values, some of which may be promises.
        /// </param>
        /// <returns type="WinJS.Promise" locid="WinJS.Promise.join_returnValue">
        /// A promise whose value is an object with the same field names as those of the object in the values parameter, where
        /// each field value is the fulfilled value of a promise.
        /// </returns>
        /// </signature>
        return new Promise(
            function (complete, error, progress) {
                var keys = Object.keys(values);
                var errors = Array.isArray(values) ? [] : {};
                var results = Array.isArray(values) ? [] : {};
                var undefineds = 0;
                var pending = keys.length;
                var argDone = function (key) {
                    if ((--pending) === 0) {
                        var errorCount = Object.keys(errors).length;
                        if (errorCount === 0) {
                            complete(results);
                        } else {
                            var canceledCount = 0;
                            keys.forEach(function (key) {
                                var e = errors[key];
                                if (e instanceof Error && e.name === canceledName) {
                                    canceledCount++;
                                }
                            });
                            if (canceledCount === errorCount) {
                                complete(WinJS.Promise.cancel);
                            } else {
                                error(errors);
                            }
                        }
                    } else {
                        progress({ Key: key, Done: true });
                    }
                };
                keys.forEach(function (key) {
                    var value = values[key];
                    if (value === undefined) {
                        undefineds++;
                    } else {
                        Promise.then(value,
                            function (value) { results[key] = value; argDone(key); },
                            function (value) { errors[key] = value; argDone(key); }
                        );
                    }
                });
                pending -= undefineds;
                if (pending === 0) {
                    complete(results);
                    return;
                }
            },
            function () {
                Object.keys(values).forEach(function (key) {
                    var promise = Promise.as(values[key]);
                    if (typeof promise.cancel === "function") {
                        promise.cancel();
                    }
                });
            }
        );
    }