Android and iPhone browser wars, Part 2: Build a browser-based application for iPhone and Android

Using HTML 5, CSS, JavaScript, Ajax, and jQuery

This article is second in a two-part "Android and iPhone browser wars" series about developing browser-based applications for iPhone and Android. In Part 1, we introduce WebKit, the browser engine at the heart of the browser in iPhone and Android. In this article, we dig deeper by building a network management application that runs on both the iPhone and Android browsers. The application demonstrates browser-local SQL storage and Ajax, key technologies that enable a rich application experience from within the mobile browser. Additionally, the application leverages the popular jQuery JavaScript library.

Frank Ableson, Software designer

Frank Ableson is an entrepreneur and software developer in northern New Jersey, specializing in mobile and embedded application software. He is the author a book about Android application development. His professional interests are embedded systems, wireless communications, and automotive electronics. His biggest fans are his wife, Nikki, and their children. You can contact him at fableson@msiservices.com.



05 January 2010

Also available in Russian Japanese Vietnamese Portuguese

Introduction

Historically, there have been a number of obstacles to developing rich-featured mobile Web applications, but the landscape is changing rapidly. The challenge of insufficient network speed has not been eliminated, but it has been vastly improved as 3G technology has become widely available for mobile phones. Likewise, the user interface has come a long way, with iPhone leading the charge, and Android gaining momentum with innovative designs, and the powerful WebKit engine providing best-in-class HTML and CSS rendering. Giving a user a lame mobile UI is no longer getting the job done.

Like the desktop browser experience, mobile Web applications have undergone a renaissance of sorts as local database storage has become available to the browser. The other technology empowering next-generation mobile Web applications is Ajax. Ajax describes the practice of using JavaScript to invoke a server-side page to retrieve specific data elements via HTTP without the necessity of retrieving and re-rendering the entire Web page. Because Ajax works in an asynchronous fashion, mobile Web applications have gained a tremendous boost in feature capability.

In this article, we target iPhone and Android. To aid in development, we will also test the application within Safari on the desktop. Because Safari is also based on WebKit, it is an ideal development platform, speeding development and providing an excellent debugging aid, thanks to the useful WebKit Inspector application.


The application

Before jumping into the specifics of the code, let's have a look at the application's goals. What do we want to accomplish with our network monitoring/management application?

If you have ever managed a Web site for customers — internal or external — you have probably received a notification that a Web site is down. It may come from an automated proactive monitoring tool, or sometimes the less-desirable variety, which is an e-mail or phone call from your customer stating, "The site is down. Check it out and get back to me ASAP."

What is the first thing typically done when you hear that a site is down? You open up your browser and try to load the home page. Perhaps the outage was related to connectivity from one location. That happens sometimes, and it pays to check that out before diving into the weeds to find a problem that doesn't actually exist in the site itself.

If we rule out a basic client-connectivity issue, the next step in triaging a site is to try to gather some key details, such as file system resources, available memory, various connectivity, last error message, etc. Doing this generally requires access to the server itself via a Remote Desktop or SSH session. But what if you are not at your desk?

This "site is down" notification is never fun to get. It invariably happens at the most inopportune of times when you are away from your office and without traditional Internet connectivity. It can be a bit of a helpless feeling. Over time, you learn that a particular site tends to fail due to one of a handful of usual suspects. The problem may be that an external resource like a database or third-party payment processor is unavailable; or perhaps a file transfer has failed, rendering a portion of the Web site with improper or out-of-date information. Whatever the challenge, there are a few key stats that would be helpful in the initial triage of a problem. Those stats or performance indicators will vary from site to site. Building a tool to help address this is the purpose of the application we will look at in this article.

The application discussed here is designed to assist with triaging Web-site problems when you are out of the office. If you have an iPhone or Android device, you've got a bit of fire power on you. We can leverage the capabilities of these powerful platforms to help manage our Web sites.


Design considerations

A design motivation of this application is to be as independent as possible from a server database — in other words, we want this application to run without the need to create an account on a third-party service. We are going to build this application in a fashion where the user need only download the Web page once and run it from their browser locally. Of course, the user can bookmark the page and refresh it as desired to obtain any new features potentially added to the application over time. However, all data is stored locally on the mobile device in a SQL database.

While there is certainly merit to having this kind of data stored on a server somewhere, the functionality to manage and store the data for multiple users is out of scope for this article. Our primary concern is leveraging local storage and Ajax to build a practical and useful application. We will conclude with some logical next steps for expanding the application.

The code for this application can be broken into a two distinct groups. We have code that runs on the mobile device and includes:

  • An index.html file that is our application shell.
  • A JavaScript file named netmon.js that contains the majority of the application's functionality.
  • Another JavaScript file named json2.js that contains JSON-related routines.
  • Multiple CSS files contain the styling information. Recall from Part 1 that much of the style information is contained in a primary CSS file. However, we have device-specific CSS files to aid in refining the look and feel of the application on a particular platform.
  • We include the jquery.js library file to aid in DOM manipulation and Ajax queries.

Then we have code that runs on the server and is unique for each site we wish to manage. The specifics of this code will vary in its implementation, but the resulting content is always the same: a JavaScript Object Notation (JSON) object that contains a few specific properties, including property-named items, which is an array of name-value pairs. The data within this JSON object dictates how it is rendered within the browser in our application. Additionally, the name-value pairs provide key operational data specific to each site, providing (hopefully) the information necessary to triage the situation quickly and help the support staff locate and resolve the issue promptly.


Building the application

We begin by looking at the data model of this application. Once we have a feel for the data model, we will examine the code that interacts with the data, bringing the application to life.

The data model

When data is stored on the mobile device via the browser, it is stored within an HTML 5 database, or a browser-accessible SQL database store. The specification for browser-based SQL data is still in flux, with its specifics still being ironed out. However, in practical terms, it is available today for our use in iPhone, Android, and other WebKit-enabled environments. This database functionality is essentially a JavaScript interface to an underlying SQLite implementation. Our database contains only a single table: tbl_resources.

The table mimics the data our application uses at runtime — essentially an array of JSON objects. Each object contains:

  • A site name.
  • The site's home page URL.
  • The site's key stats URL; we call this the ping URL.
  • The site's current status: either OK or BAD.
  • The site's current summary, which is a short piece of text describing the current condition of the site. This might look like the database is down, for example.
  • An array of name-value pairs that contain site-specific details to assist in describing the current operational conditions of the site. Note that these values may be of value even when a site is not down. A particular element may contain data that gives insight to an impending problem in the near future.

Listing 1 is an example JSON object representing a site.

Listing 1. JSON object representing a site
    [
      {
         name : 'msi-wireless.com',
         homeurl : 'http://msiservices.com',
         pingurl : 'http://ibm.msi-wireless.com/mobile2/netmon.php',
         status : 'OK',
         summary : 'Everything is fine...',
         items :
         [
          {name : 'DiskSpace', value : '22.13 GB'},
          {name : 'Database Up?', value : 'Yes'}
         ]
      }
   ]

The role of the database is to persist the data over time, though manipulation is done via an array containing these objects, rather than referring to the database continually. This strategy was implemented to make things a bit more simplistic in the JavaScript and to minimize the number of times data was going in and out of the database. However, for an application with large numbers of data elements, it may be advantageous to work directly with the database, or more likely in some sort of "paged" fashion, where a number of elements are taken from the database at a time and a JavaScript array would contain a "window" of elements, representing a subset of the full number of items.

Figure 1 contains a snapshot of the database structure, along with a few records. Viewing the database can be accomplished with the Web Inspector, which is part of the Safari/WebKit browser platform. This is one of the reasons why developing with WebKit is powerful. We are looking here at the Web Inspector on the desktop. All of the code works fine on iPhone and Android, as well.

Note: Android V2.0 was the target of this code. The WebKit feature set in Android has been maturing with each release.

Figure 1. Snapshot of the database structure and a few records
Screenshot shows a snapshot of the database structure and a few records

Now that we have a feel for what the data elements look like, how do we manage them with our application?


The client side — The default view

The default UI for the application is a list of the sites under management, sorted by their status value. Sites that are not OK are shown first, as seen in Figure 2.

Figure 2. Sites that are not OK
Image shows sites that are not OK

We see that there are three sites under management. At present, we have two sites indicating a problem. If a site is in good condition, meaning its status property is equal to OK, we do not display the summary field, and it is displayed in black text. If the status is equal to BAD, we display the summary next to the name of the site and a style named BAD in the CSS file dictates the rendering attributes — red text in this example. For more details, see the file netmon.css; the full source code for this application is available in the Download section.

By clicking on an entry, the details for that entry toggle between visible and hidden. As seen in Figure 2, each entry has three links available, followed by the home and ping URL locations, then a section listing the details for the site, which is to say the list of name-value pairs representing the conditions for that site.

In this example, we see that the site named ibm demo 2 has a summary of "No more coffee?" This is of course not necessarily a technical emergency, but it provides a fun example to consider. Jumping down to the Details section for this entry, we see the key statistic behind this server condition: The Coffee Pot is empty.

We see that we could hit the home page link, which will launch a new browser window. Second, we can refresh the data by tapping on the Refresh link. We will examine the Refresh step in a moment.

Last, we can remove this entry by selecting the Remove link. A simple window.confirm() query asks us to confirm if we want to carry out this non-reversible task.


The client side -- The HTML

To create this list, we need to examine two files in some detail. The first is index.html, shown in Listing 2, and the second is netmon.js, shown in Listing 3. Within this article, we will look at some snippets along the way, though you will want to refer to the Download section for the full source code.

We are still specifying the mobile WebKit viewport meta to help guide the browser on how we prefer the page to be rendered. We also use some selective JavaScript to specify a particular CSS file to target a specific device.

What is new is the inclusion of some JS files along with some "local" JavaScript files. The file is rounded out with a new HTML div with an ID of entryform, which contains the elements necessary to add a new entry directly from within the application. There is no need to load a different HTML page, which is the traditional approach to accomplishing this task. Recall that one of our design objectives is that our application not rely upon additional server-based tools for data manipulation or storage outside of the sites we are monitoring. Listing 2 contains the include statements from index.html.

Listing 2. index.html
<link rel="stylesheet" href="netmon.css" type="text/css" />
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="netmon.js"></script>
<script type="text/javascript" src="json2.js"></script>

<script type="text/javascript">
   if (navigator.userAgent.indexOf('iPhone') != -1) {
      document.write('<link rel="stylesheet" href="iphone.css" type="text/css" />');
   } else if (navigator.userAgent.indexOf('Android') != -1) {
      document.write('<link rel="stylesheet" href="android.css" type="text/css" />');
   } else {
      document.write('<link rel="stylesheet" href="desktop.css" type="text/css" />');
   }

The client side — Populating the list

Data is pulled from our local database and displayed in a list of server entries. To get the list, we open the database and issue a SQL query to fetch all the items within our database table.

A common technique in applications of this variety is to create tables on the fly. Remember; We have no DBA around to help us. Our application must do all the work on its own. Listing 3 contains the source to netmon.js: the file that implements a number of items, including:

  • Definition of a data-type constructor for the netmonResource object.
  • A group of functions required to assist the application in interacting with the database and the to-be-monitored Web sites. This is implemented in the netmon namespace and contains:
    • dbHandle— A function used to manage our connection to the database.
    • initialize— A function used to create/open the database and retrieve the list of servers we are managing. Listing 3 contains this function, which attempts to open the database (if not already opened) and query the database. If the database table is not present, this function initiates the process of creating that table.
    • createResourcesTable— A function that creates the required database table and creates a single default entry for demonstration purposes.
    • addEntry— A function that inserts a new entry to the database table.
    • deleteEntry— A function that removes an entry from the database table.
    • refreshEntry— A function that refreshes the entry by retrieving the entry's ping URL via an Ajax call.
    • Resources— An array containing a "cache" of our database entries. Some of the other functions work on this array, rather than the database directly.
    • Render— Function that returns an HTML string, formatted according to some rules we devise on how we prefer each server entry to be displayed. If we come up with new formatting ideas, they would be implemented in this function, along with any required CSS styles.
Listing 3. netmon.js

Click to see code listing

Listing 3. netmon.js

  initialize : function () {
		try {
         if (netmon.dbHandle == null) {
		      netmon.dbHandle = openDatabase("netmondb","1.0","netmon",1000);
		   }
		   
		   $("#mainContent").html("");
	      netmon.dbHandle.transaction(function(tx) {
			   tx.executeSql("SELECT * FROM tbl_resources order by status,nme",
			     // parameters
				 [],
				 // good result
				 function(tx,result){
				 	try {
				 	   var items = new Array();
						for (var i = 0; i < result.rows.length; i++) {
							var row = result.rows.item(i);
							items[i] = new netmonResource();
							items[i].name = row['nme'];
							items[i].homeurl = row['homeurl'];
							items[i].pingurl = row['pingurl'];
							items[i].status = row['status'];
							items[i].summary = row['summary'];
							items[i].items = eval(row['items']);
							if (items[i].items == undefined) {
							   items[i].items = [];
							}
						}
						netmon.resources = items.slice(0);
		   // setup gui with our data				
                  if (netmon.resources.length > 0) {
                     jQuery.each(netmon.resources,function (index, value) {
                        $("#mainContent").append(netmon.render(index,value));
                     });
                     $(".serverentry").click (function() {$(this).find(".serveritems").toggle();});
                     $(".serveritems").hide();
                  }
					} catch (e) {
					  alert("Error fetching rows from database..." + e);
					}
				 },
				 function(tx,error){
				 	//alert("bad result on query: " + error.message + " 
                               let's try to create the db table");
					netmon.createResourcesTable();
				 }
				 ); 
   		});
		}catch (e) {
		   alert("error opening database.  " + e);
		}
      
   },
...
   render : function(index,itm) {
      try {
         var ret = "";
         ret += "<div class='serverentry " + itm.status + " " + (index % 2 == 0 ? 'even' : 'odd') + "'>";
         //ret += "<span class='name'>" + itm.name +
         "</span>&nbsp;&nbsp;<a target='_blank' href='" +
         itm.homeurl + "'>Show</a><br /><a target='_blank'
         href='javascript:netmon.deleteEntry(\"" + itm.name + "\");'>Remove</a><br />";
         ret += "<span class='name'>" + itm.name + "</span>";
         if (itm.status != "OK") {
            ret += "<span class='summary'>-" + itm.summary + "</span><br />";
         }
         
         ret += "<div class='serveritems'>"; 
         ret += "<a target='_blank' href='" + itm.homeurl + "'>Home</a>&nbsp;&nbsp;
         <a target='_blank' href='javascript:netmon.refreshEntry(" + index + ",false); '>Refresh</a>&nbsp;&nbsp;
         <a target='_blank' href='javascript:netmon.deleteEntry(\"" + itm.name + "\");'>Remove</a><br />";
         ret += "Home URL:&nbsp;" + itm.homeurl + "<br />";
         ret += "PING URL:&nbsp;" + itm.pingurl + "<br />";
	 ret += "<hr />Details<br />";
         jQuery.each(itm.items,function (j,itemdetail) {
            ret += ">>" + itemdetail.name + "=" + itemdetail.value + "<br />";
         });
         ret += "</div>";      
         ret += "</div>";
         return ret;
      } catch (e) {
            return "<div class='error'>Error rendering item [" + itm.name + "] " + e + "</div>";
      }
   }
};

The namespace netmon contains much of the logic we need to implement the site, augmented by some helper functions in the index.html file. Let's take a look at the task of adding a new entry to the database.


The client side — Adding a site

The application manages a list of one or more sites of interest to the user. For each site, we initially collect and store:

  • The site name
  • The home URL
  • The ping URL

To accomplish this, we create a simple form, containing three fields, their labels, and a couple of buttons, as shown in Figure 3.

Figure 3. Simple form
Screen shows a simple form

This form content is actually contained within the same index.html page, as shown in Listing 2. We switch between the default list view and this add-new-server form with some jQuery magic. The list of site entries is contained within an HTML div with an ID of mainContent. The form is contained within an HTML div with ID of entryform, which is initially hidden. At any point in time, the visibility of these two div elements is mutually exclusive. To swap their visibility, we call the jQuery method named toggle on each of these elements as shown in the function addNewEntry, shown in Listing 4.

Listing 4. The addNewEntry function
function addNewEntry() {
   $("#entry_name").val("");
   $("#entry_homeurl").val("");
   $("#entry_pingurl").val("");

   $("#mainContent").toggle();
   $("#entryform").toggle();
}

When a new server entry has been made, we need to save it to the database and refresh our list of servers to reflect the new entry's inclusion in the list. The user initiates this by selecting the Save button, which invokes the saveEntry JavaScript function implemented in index.html. This function creates a new JavaScript object called netmonResource. We validate that all of the fields have been properly filled out, then initiate the saving to the database. We save the new entry by invoking netmon.addEntry. Afterward, we jump back to the list of entries by again calling toggle on the two primary div elements. Note: We did not implement full regex to validate the URLs, which would be a good idea.

For the following discussion, refer to Listing 3: netmon.js.

Using the SQL database interface in JavaScript requires two basic steps. First, we need to open the database if it was not previously opened. In this application, we open the database in the initialize function. To create a new record in the database, let's examine the code in the addEntry function, shown in Listing 5. An SQL transaction is initiated by calling <databasehandle>.transaction(function (tx) {tx.executeSql()});. In our case, the database handle is netmon.dbHandle.

The executeSql function takes four arguments:

  1. A SQL statement in which any parameters, such as field values, are represented with a placeholder of ?. Each ? is replaced with a value, based on ordinal position contained in the second argument to the function.
  2. An array of values used as parameters, replacing the ? placeholders as indicated in the SQL statement in the first argument.
  3. A callback function invoked if the statement is executed successfully. The arguments to this callback function include a transaction handle and a result set.
  4. A callback function invoked if the statement encountered an error. The arguments to this callback function include a transaction handle and an error object.

Listing 5 shows the function netmon.addentry.

Listing 5. The netmon.addentry function

Click to see code listing

Listing 5. The netmon.addentry function

   addEntry : function (entry) {
      try {
         netmon.dbHandle.transaction(function(tx) {
            tx.executeSql("insert into tbl_resources (nme,homeurl,pingurl,status,summary,items) values (?,?,?,?,?,?)",
               [
                  entry.name,
                  entry.homeurl,
                  entry.pingurl,
                  entry.status,
                  entry.summary,
                  JSON.stringify(entry.items)
               ],
               function (tx,results) {
                //  alert(entry.name + " added.");
                netmon.initialize();
               },
               function (tx,error) {
                  alert("Error in addEntry [" + error.message + "]");
               });
            });
      }catch (e) {
         alert("Error in netmon.addEntry " + e);
      }
   }

We now have the ability to add entries. Deleting entries from the list is very similar. Refer to the netmon.deleteEntry function to see how an entry is deleted from the database.

It is time to look at how we refresh entries with Ajax.


The client side — Refresh with Ajax

As described, Ajax is a technique to retrieve server-side content without requiring the entire Web page to be refreshed. This is implemented with different underlying technologies depending on the browser. However, we have taken the approach to rely upon the JavaScript library jQuery to hide this from us. Yes, this is an article on iPhone and Android browser development, but in this day, it is very common and accepted — even encouraged — to utilize a JavaScript library. When you see just how easy this is in jQuery, you will likely agree.

The netmon.refreshEntry function contains our Ajax code, shown in Listing 6, as well as some code to update the database with the resulting data.

Listing 6. The netmon.refreshEntry function

Click to see code listing

Listing 6. The netmon.refreshEntry function

   refreshEntry : function (entryidx,bfollow) {
      try {
         //alert("refresEntry [" + netmon.resources[entryidx].name + "][" + netmon.resources[entryidx].pingurl + "]");
         $.get(netmon.resources[entryidx].pingurl,
            function (data,textstatus) {
              // alert("response is here : [" + data + "]");
               try {
                  var response = eval(data);

                  netmon.dbHandle.transaction(function(tx) {
                     tx.executeSql("update tbl_resources set status = ?,summary = ?,items = ? where nme = ?",
               [
                  response[0].status,
                  response[0].summary,
                  JSON.stringify(response[0].items),
                  netmon.resources[entryidx].name
               ],
               function (tx,results) {
                  netmon.initialize();
                  if (bfollow) {
                     if (entryidx + 1 < netmon.resources.length) {
                        netmon.refreshEntry(entryidx + 1,true);
                     }
                  }
               },
               function (tx,error) {
                  //alert("Error in refreshEntry [" + error.message + "]");
                  if (bfollow) {
                     if (entryidx + 1 < netmon.resources.length) {
                        netmon.refreshEntry(entryidx + 1,true);
                     }
                  }
                  
               });
            });

               } catch (e) {
                  alert("error handling response [" + e + "]");
               }
            }
            );
         
      } catch (e) {
         alert("Error refreshEntry " + e);
      }
      
   }

To get data from the server, we simply invoke $.get(). The first argument is the URL, and the second is a function to invoke when the call is completed. In our example, we update the database with the newly retrieved data (see Listing 3: netmon.refreshEntry) by using some of the database skills previously discussed.

jQuery also provides for the facility to supply "form-encoded data" for passing parameters to a Web page, as well as providing a hint on what kind of data is expected back. See Resources for a reference to jQuery's Ajax functions. There is additional granularity to be achieved, depending on your application's needs.

Note that jQuery also provides a method for retrieving a JSON object directly. However, we chose to use the "generic" Ajax function, instead, to be more generally applicable to other applications you might prefer to build.

When we receive data back from the server, we want to turn it into a JavaScript object to access its properties. We can do this simply with the JavaScript function named eval: var object = eval(<json text>);.

At this point, we are pretty well along with the client-side functionality. There remains one more item to cover: the server-side pages, which are responsible for generating the JSON our application expects to be processing.


The server side — Build your own monitor page

We described the JSON structure our application is expecting back, but just how does this get generated? The short answer is that it is up to you. If you prefer to work in .NET technologies, you can generate a .aspx page to dynamically generate the data. Alternatively, you can have a periodically scheduled shell script generate a text file you simply retrieve to find out the most recently updated status. The bottom line is that the application needs to be able to fetch a JSON object based on a single URL, but you accomplish the task of generating the JSON is up to you.

The sample PHP file shown in Listing 7 generates a list of files and selectively looks for the file named error.txt. If this file is not empty, we assume that there is a problem of some sort and construct our JSON object to indicate a BAD status value and use the contents of the error.txt file to update the summary property.

Listing 7. Sample PHP file
<?
$filecount = 0;
$statusValue = "OK";
$summaryValue = "No Error";
?>
[
{
items : [
{ "name" : "Date", "value" : "<? echo date("m/d/y H:m:s"); ?>"  },
<?
foreach (new DirectoryIterator("./data") as $fileInfo) {
   if ($fileInfo->isDot() || $fileInfo->isDir()) continue;
   $filecount++;
   echo "{\"name\" : \"".$fileInfo->getFilename()."\",\"value\" : 
\"".$fileInfo->getSize()."\"},";
   if ($fileInfo->getFilename() == "error.txt") {
     if ($fileInfo->getSize() > 0) {
        $statusValue = "BAD";
        $fsize = $fileInfo->getSize();
        $fh = fopen("./data/".$fileInfo->getFilename(),"r");
        $summaryValue = fread($fh,$fsize);
        fclose($fh);
     }
   }
}
?>
 {"name" : "FileCount","value" : "<? echo $filecount ?>"}
],
"status" : "<? echo $statusValue ?>",
"summary" : "<? print str_replace("\n","",$summaryValue) ?>"
}
]

This code generates the following JSON data shown in Listing 8, assuming an error condition is present.

Listing 8. JSON data generated by Listing 4
[
    {
        "items" : [
            {
                "name" : "Date",
                "value" : "11/22/09 15:11:51" 
            },
            {
                "name" : "error.txt",
                "value" : "20" 
            },
            {
                "name" : "bad.html",
                "value" : "91" 
            },
            {
                "name" : "testfile.txt",
                "value" : "44" 
            },
            {
                "name" : "good.html",
                "value" : "87" 
            },
            {
                "name" : "FileCount",
                "value" : "4" 
            } 
        ],
        "status" : "BAD",
        "summary" : "the sky is falling." 
    } 
]

In this example, we provide the date at which the data was generated, a current count of the files, as well as a list of the files and their sizes. Keep in mind that the time zone for your server may differ from your localized location.

Because error.txt is not zero bytes in length, we read in the content of that file and assign it to the summary property. That's it — a very basic monitoring script.

As you work on generating your JSON, you may find that you have a problem parsing the code from within your browser-based application. If this is the case, you may want to use a JSON validation tool (see Resources). Figure 4 shows this entry rendered in the desktop browser.

Figure 4. JSON validation tool
Figure 4 shows the JSON validation tool

Figure 5 shows the application running in iPhone.

Figure 5. Application running in iPhone
Image shows application running in iPhone

Figure 6 shows the application running in Android with slighgly different server lists.

Figure 6. Application running in Android
Figure 6 shows the application running in Android

The application is now complete — more or less. There are always new things that could be added, so in the next and final section, we'll look at a list of things left as an exercise for you.


Next steps

There is no shortage of new ideas that can be thought up to make this application better. Here are a few items that would be fun to add:

  • Edit your entries — Presently, you can only Add New or Delete.
  • Implement some regular expressions to properly validate form field entries.
  • Send information to a colleague via e-mail directly from the application.
  • Store profiles on a server so you can move from device to device.
  • Incorporate this application into a native application with a tool like PhoneGap or Appcelerator. This would permit you to sell the application in the AppStore.

Summary

This article built upon the foundational knowledge from Part 1 to build a highly functional mobile Web application using SQL local storage and Ajax queries. The application itself can be used as a tool to assist in monitoring your current networks with just a little work on the server side.

Hopefully, you are inspired to build your own mobile applications for iPhone and Android.


Download

DescriptionNameSize
Part 2 source codeos-androidiphone2-browserwars2.zip31KB

Resources

Learn

Get products and technologies

Discuss

  • The Eclipse Platform newsgroups should be your first stop to discuss questions regarding Eclipse. (Selecting this will launch your default Usenet news reader application and open eclipse.platform.)
  • The Eclipse newsgroups has many resources for people interested in using and extending Eclipse.
  • Participate in developerWorks blogs and get involved in the developerWorks community.

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Open source on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Open source, Industries
ArticleID=459785
ArticleTitle=Android and iPhone browser wars, Part 2: Build a browser-based application for iPhone and Android
publish-date=01052010