Managing Network Availability in a Phonegap Application

I have just completed my first distributed Android tablet application. One of the main requirements of the application was for it to run in both offline and online mode. When starting out I assumed that I would just check to see if there was network availability using the following code that I got from the Phonegap site. 

function reachableCallback(reachability) {
        // There is no consistency on the format of reachability
        var networkState = reachability.code || reachability;
 
        var states = {};
        states[NetworkStatus.NOT_REACHABLE]                      = 'No network connection';
        states[NetworkStatus.REACHABLE_VIA_CARRIER_DATA_NETWORK] = 'Carrier data connection';
        states[NetworkStatus.REACHABLE_VIA_WIFI_NETWORK]         = 'WiFi connection';
 
        alert('Connection type: ' + states[networkState]);
    }

What I soon found out is that this did not cover all of the scenarios. There is a third scenario that occurs which is much harder to identify. There can be a network connection but the strength of that signal can be very low. When connecting to the rest services using JQuery we would set the time out on the connection to 20 seconds, but what we found is that if the device was getting data back at all it would continue to leave the connection open even past the 20 seconds. In order to keep the application moving I needed to identify this slow connection and run the application in offline mode.

The following code calls out to an image that is the average size of the service responses that our mobile application calls out to. I combine that with a javascript timeout that stops the processing of the success callback if the timeout occurs. Check out the code below.

function(onlineFunction,offlineFunction)
{
    var timeOutInteger = null;
    var timeOutOccured = false;
 
      var net_callback = function(reachability) {
 
        // There is no consistency on the format of reachability
            var networkState = reachability.code || reachability;
 
            var states = {};
            states[NetworkStatus.NOT_REACHABLE]                      = 'No network connection';
            states[NetworkStatus.REACHABLE_VIA_CARRIER_DATA_NETWORK] = 'Carrier data connection';
            states[NetworkStatus.REACHABLE_VIA_WIFI_NETWORK]         = 'WiFi connection';
 
            if(states[networkState] == 'No network connection')
            {
                if(!timeOutOccured){
                    clearTimeout(timeOutInteger);
                    offlineFunction();
                }
            }
            else
            {
                var url = "http://yourservicehere/LeadWeight.jpg";
 
                    $.ajax({ type: "GET",
                            data: "{}",
                            url: url,
                            cache: false,
                            timeout: 20 * 1000,
                            success:function(response)
                            {
                                if(!timeOutOccured){
                                    clearTimeout(timeOutInteger);
                                    onlineFunction();
                                }
                            },
                            error:function(xhr, textStatus, errorThrown) {
                                if(!timeOutOccured){
                                    clearTimeout(timeOutInteger);
                                    offlineFunction();
                                }
                            }
                        });
            }
      };
 
      navigator.network.isReachable("www.google.com", net_callback);
 
      timeOutInteger = setTimeout( function() {
                timeOutOccured = true;
                                offlineFunction();
                             }, 20 * 1000 );
}

The function is a little hard to read, but it takes two methods as arguments. It takes an online function and an offline function which are pretty self explanatory. The first code that runs is actually at the bottom of the function. I first make a call to the isReachable() function and assign the callback. This is an asynchronous function that waits for a callback. Immediately after kicking off the isReachable call I call the javascript setTimeout function. It takes 2 arguments. A function that fires if the timeout occurs and the actual timeout. The setTimeout function returns an integer that can be used to cancel the timeout. I store this integer in a method level variable so that we can cancel the time out in the call back. If the time out occurs I set the method level variable timeOutOccured to true. This variable is used throughout the isReachable callback to check to see if the timeout has already occurred. Since both of these functions are asynchronous the variable helps us stop processing when the time out occurs during mid processing. As we explore the isReachable callback we will see that variable being used.

Inside the isReachable callback I first check to see if there is a connection. If there is not a wifi or carrier connection I cancel the timeout and run the offline function. The next case is the more complicated case. If the isReachable callback returns with a connection, I then attempt to download an image that is located on the server where I my web services reside. I use JQuery to make the ajax call. If the call to download the image comes back successful I check to make sure that the timeout hasn’t already occurred, I cancel the timeout and then run the online function. If the ajax call comes back with an error I check to make sure the timeout hasn’t occurred, cancel the timeout and run the offline function.

This method could be susceptible to some timing issues, but for the most part has held up very well and allowed our Android + Phonegap application to run successfully in both online and offline mode. In the application I have pulled out the timeout into a configuration variable, but for the purposes of this post I have it just hard coded into the method.

Any comments, suggestions or ways to make this method even better are welcome!