Home » Blog » Build a Contacts Application With jQuery Mobile & the Android SDK – Part 1

Build a Contacts Application With jQuery Mobile & the Android SDK – Part 1

원문보기

In this tutorial series, we will develop a native Android application where the user interface is implemented using the jQuery Mobile framework. For this purpose, we will introduce a model for representing UI screens in a native Android application using jQuery Mobile page elements, in particular, container and content pages. Although the tutorial code is developed for contacts management, the basic concepts of the model are generic and can be applied to applications where the UI is suitable for constructing via HTML and JavaScript.

As part of the model, we discuss integrating JavaScript with back-end Java code and describe accessing Java methods from JavaScript and, conversely, accessing JavaScript/HTML from Java.

A summary of the main aspects of the model to be created are as follows:

  • A physical HTML page contains one container page with one or more content pages where each content page represents one or more UI screens.
  • Accessing Java from JavaScript is based on JavaScript binding of Java objects, made possible via the addJavascriptInterface() method in theandroid.webkit.WebView class.
  • Accessing JavaScript/HTML from Java is accomplished via the loadUrl()method in the android.webkit.WebView class. That method can be used to call a JavaScript function or to load a physical HTML page.
  • Java back-end code works as a service for the JavaScript code in the UI. JavaScript code invokes a Java method to request a particular activity and specifies a callback JavaScript function to receive the results back. In certain cases, JavaScript code invokes a Java method for controlling navigation, which is described next.
  • Screen navigation is managed by the JavaScript code according to the following rules.
    • Transition from one content page to another within the same container page is performed by JavaScript code.
    • Transition from one container page to another is performed by Java code using WebView.loadUrl(). The container page to display is passed to Java code from JavaScript as a parameter.
  • When passing an object from a JavaScript function to a Java method, or vice versa, JSON object representation is used.

Additional aspects of the tutorial are as follows.

  • The UI elements for the tutorial application are constructed with jQuery Mobile list views, list dividers and collapsible blocks. We use jQuery Mobile version 1.0 Alpha 3. (An Alpha 4 release was recently made with various bug fixes. See the announcement.)
  • According to the Android API, any non-UI thread that invokesWebView.loadUrl() should use a android.os.Handler object to access theWebView instance. We explain how we fulfilled that requirement in our tutorial application.
  • We utilize Android contacts API for reading and manipulating contacts in anAndroid device. Various database tables that store basic elements of a contact are explained, such as phones, addresses, emails, etc. We describe how to query those tables using Android API.
  • We discuss how to develop the project in Eclipse IDE with Android Development Tools plugin. Project configuration files are described and step-by-step instructions are given to import the project source code into the Eclipse development environment.

This tutorial series has been organized as follows. In Part 1, ‘Screen Flow’ we introduce the screens in the application and describe transitions between those screens. The section ‘jQuery Mobile Page Structure’ gives an overview of page elements in jQuery Mobile framework and explains how the screens in this application are constructed using those elements. In ‘Integrating HTML/JavaScript Based UI With Android Java Back-end’, we review basic concepts in integrating UI, composed of HTML and JavaScript, with the back-end Java classes that implement the business functionality. The subsections here are ‘Accessing Java From JavaScript’, ‘Accessing HTML/JavaScript From Java’ and ‘Parameter Data Types In Method Calls’. In ‘Application Startup’, we begin review of the code for particular functional areas of the application. In each section, we describe the sequence of events that take place between UI and Java back-end to perform certain activities.

We start Part 2 of the tutorial with the section named ‘Create Account’, which describes how to create an account for associating with the newly created contacts. In ‘Existing Contact’, we describe editing and deleting an existing contact. Part 3 of the tutorial starts with ‘Adding Contact’, which explains how to add a brand new contact. In ‘Reading Contacts Via ContactUtility Class’, we discuss how to use Android Java API for accessing and manipulating contacts in an Android device.

In Part 4, the last installment of the tutorial, ‘Write Operations For Contacts’ explains deleting and saving a contact using Android Java API. This section, as well as ‘Reading Contacts Via ContactUtility Class’ (Part 3), describe how to use Jackson JSON Processor for converting back and forth between Java data objects and JSON formatted string representations of those objects. In ‘Project Configuration And Supporting Files’, we discuss the configuration files for the project. The section ‘Application Launch Icon’ describes how we generated the launch icons for the tutorial application according to the Android Icon Design Guidelines. In ‘Native Android Application Development Environment’, we describe the development environment for the application and explain individual steps for importing the project into Eclipse IDE. Finally, ‘Conclusions’ gives the concluding remarks for this tutorial.

In our application, every contact should be associated with an account. Typically, accounts are identifiers for a user in an online service such as ‘gmail’. For this demo application, we will create an account that will own all the contacts created by the application. For simplicity, that account will not be related to any online service. Contacts that belong to other accounts will still be displayed via the application, however, they cannot be edited or deleted. (This restriction is intentional to avoid accidental damage to contacts in an actual Android device.) When application starts, it checks if an account has been created for the application. When running for the first time, the account does not exist, therefore, user is displayed a ‘Create Account’ screen (Figure 1 below).

Create Account Screen

Figure 1. Create Account Screen.

Once account is created, the initial screen to be displayed when the application starts is the ‘Contact List’ screen. The ‘Contact List’ screen gives a listing of all contacts in the device (Figure 2 below).

Contact List Screen

Figure 2. Contact List Screen.

If user clicks the add button, an ‘Empty Contact’ screen is shown. On that screen, user can type in new contact information and save the contact. Once the contact is saved, the ‘Contact List’ screen is displayed with the newly added contact. On ‘Empty Contact’ screen user can cancel entering the new contact. In that case the ‘Contact List’ screen is displayed without saving the contact. The ‘Empty Contact’ screen is shown in Figure 3, left, below.

Empty Contact and Existing Contact Screens

Figure 3. Empty Contact (left) and Existing Contact (right) Screens.

On ‘Contact List’, user can also select an existing contact and view its details on ‘Existing Contact’ screen, as shown in Figure 3, right, above. Here, user can edit the contact and save the changes, which returns the user to ‘Contact List’ screen with updated contact information. On ‘Existing Contact’, if cancel is selected, user is taken back to ‘Contact List’ and no changes are made to the contact information. The ‘Existing Contact’ screen is identical to ‘Empty Contact’ except that the contact details is filled in with existing contact information.

On ‘Existing Contact’, user can also press Delete button to delete the contact. When Delete button is pressed, the ‘Confirm Delete’ screen is displayed, Figure 4 below. On ‘Confirm Delete’, if Delete is pressed the contact is deleted and the user is taken back to ‘Contact List’ screen. On ‘Confirm Delete’ if Cancel is pressed, no action is taken and user is taken back to ‘Existing Contact’ screen.

Confirm Delete Screen

Figure 4. Confirm Delete Screen.

In screen transitions that require a certain amount of data processing, the application displays a ‘Progress’ screen as user stands by (Figure 5 below). The Progress screen is always displayed before any of the ‘Create Account’, ‘Contact List’, ‘Empty Contact’ and ‘Existing Contact’ screens is shown.

Progress Screen

Figure 5. Progress Screen.

Summarizing the discussion above, the following figure shows the screen flow in our application.

Screen Flow

Figure 6. Screen Flow.

Let us discuss how the screens in our application will be represented using the jQuery Mobile page elements. (For a detailed discussion on how pages are constructed in jQuery Mobile framework, see the section in reference documentation named Anatomy of a Page.) A single HTML file in jQuery Mobile framework consists of one container page with one or more content pages inside the container. The container page has data-role="page" attribute whereas the content pages havedata-role="content" attribute. A content page can optionally have header and footer. In the example below, there are two content pages with header and footer and one content page without header or footer.

Within a container page, it is intended to display only one content page at a time. The idea is to group logically related screens together inside a single container page and define each of those as a content page. For example, those content pages may share common JavaScript code. Another example is one where a main content page is grouped together with several other content pages that represent dialogs related to main content.

In this tutorial application, each of ‘Create Account’ (Figure 1), ‘Contact List’ (Figure 2) and Progress screens (Figure 5) is represented as a content page in a container page which is stored in the physical page ListPage.html. Similarly, the physical page DetailPage.html has a container page with three content pages in it; one of those content pages is used to represent both ‘Empty Contact’ and ‘Existing Contact’ screens (Figure 3), another content page represents ‘Confirm Delete’ screen (Figure 4), and the third content page represents the Progress screen. The following figure summarizes this discussion.

Page Structure

Figure 7. Page Structure.

Displaying/hiding of content pages is done programmatically. This will be shown during review of the code below.

In this tutorial, we are developing a native Android application where UI is implemented with jQuery Mobile framework which consists of JavaScript and CSS libraries. The jQuery Mobile framework aims to help develop mobile web applications with uniform user experience across different mobile platforms, devices and browsers. As such, it does not have a specific integration mechanism, e.g. a plug-in, with Android out of the box. For this reason, integration of jQuery Mobile with Android must be custom developed.

Before starting a review of the HTML pages and JavaScript, as well as Java code of this tutorial, it makes sense to look at three main aspects of that integration:

  1. Accessing Java from JavaScript
  2. Accessing HTML/JavaScript From Java
  3. Parameter Data Types In Method Calls Between Java-JavaScript

Android provides a generic way of exposing Android Java objects in JavaScript code using addJavascriptInterface(Object object, String interfaceName) method inandroid.webkit.WebView class. Basically, this method allows invoking public methods of a Java object from JavaScript code running in WebView. Note thatWebView is a special kind of android.view.View built on WebKit and allows displaying web pages consisting of HTML and JavaScript. Whether they reside over the Internet or packaged together with the native Android application, those web pages are referenced via URLs for the WebView to find and display them. (Interested reader is referred to the original API documentation for more information onWebView and View.)

The figure below shows how a Java object is accessed by JavaScript code running in WebView where the HTML page is part of the Android Application Package. The Java object is exposed to JavaScript code as a JavaScript object.

Java - JavaScript Binding

Figure 8. Java – JavaScript Binding.

The example listing below shows how to expose a Java object, an instance ofContactsActivity class, as a JavaScript object. In Activity.onCreate(), we first initialize the WebView instance. Then, the ContactsActivity object registers itself as a JavaScript object under name contactSupport. The ContactsActivity class has various public methods, e.g. deleteContact().

The code snippet below shows an example of accessing the ContactsActivityobject from JavaScript code. It is as simple as calling any public method of the object using registered name of the object.

In this tutorial application, we will use loadUrl(String url) method in WebViewclass for two different purposes: (1) load a new HTML page in WebView and (2) invoke a JavaScript method on the currently loaded HTML page in WebView. Putting those in perspective with the jQuery Mobile container and content pages, we adhere to the following rules.

  • Transition from one content page to another within the same container page is performed by JavaScript code.
  • Transition from one container page to another is performed by Java code usingWebView.loadUrl(String url). The container page to display is passed to Java code from JavaScript as a callback parameter.

In addition to loading a new HTML page, the WebView.loadUrl(String url) method is also used to invoke a JavaScript method on the currently loaded HTML page.TheJavaScript method to invoke is passed to Java as a callback parameter from JavaScript code. For any JavaScript method that invokes a Java method, the diagram below summarizes the interaction between Java and JavaScript.

  • Upon user stimulus or loading of the HTML page, the JavaScript code first displays the Progress screen.
  • Then, the JavaScript code performs some data processing, depending on the particular scope, and invokes a Java method. The Java method is passed parameters to process (optional) and either a JavaScript function to call back or an HTML page to load, after the Java method completes processing its business logic.
  • The Java method completes processing its business logic and then calls back the JavaScript function or loads the HTML page as requested.
Java - JavaScript Interaction

Figure 9. Java – JavaScript Interaction.

Now let us look at how Java code implements those interactions.

The code listing below gives an example of a Java method, deleteContact(). That function is called by JavaScript with id of a contact to be deleted and name of an HTML page that should be displayed after contact is deleted. (The id of a contact is unique identifier for that contact in contacts database of the Android device.)

Firstly, let us look at how we use android.os.Handler. The Android reference forandroid.view.View states that:

“…You must always be on the UI thread when calling any method on any view. If you are doing work on other threads and want to update the state of a view from that thread, you should use a Handler.”

What this implies to us is that the deleteContact() method could not directly callWebView.loadUrl(). This is because of two reasons. Firstly, the WebView instance is created in the onCreate() method. Secondly, when the deleteContact() method is called by JavaScript, the thread that executes that method is different than the one that executed the onCreate() method of ContactsActivity. This is evident from the Android reference for WebView.addJavascriptInterface() method where it says “…The Java object that is bound (to JavaScript) runs in another thread and not in the thread that it was constructed in.”

This is where android.os.Handler is helpful. From the Android reference, aHandler object allows us to execute an action (WebView.loadUrl() in this case) in the particular thread that is associated with the Handler object. In onCreate()method, we initialize both the WebView and Handler objects and therefore both those objects are associated with the same thread.

With that concept of Handler in mind, the listing above should now be straightforward to understand.

  • In deleteContact(), we delete the contact first (the ContactUtility class will be reviewed later on).
  • Then, we call the loadPage() method which simply puts a prefix in front of the name of HTML page to be displayed, converting name of the page to afile:// based URL.
  • Finally, we call the loadURL() method to have the Handler object call theWebView.

Having reviewed an example where Java code displayed an HTML page after request processing, let us look at another example where Java code invokes a JavaScript method after processing a request, to pass its results back.

The getAllContacts() method generates a list of all contacts in JSON format and passes back the result to a specified callback JavaScript function. The method is passed two JavaScript callback function names. The first one should be used to pass the list of contacts. The second one is used only after initial installation where an account for the tutorial application has not been created yet. Invoking that second JavaScript function should guide the user to create an account.

The JavaScript code listing invoking ContactsActivity.getAllContacts() method is given below.

The setContactsList() function is one that takes a listing of contacts in JSON format, parses it and displays the ‘Contact List’ screen in Figure 2 above. TheshowAccount() function displays the ‘Create Account’ screen in Figure 1 without any data processing. We will provide review of those functions later.

In this application, we only use String data type during function calls back and forth between JavaScript and Java. Other primitives, such as Integer, are passed as a String and cast to the proper type later on. Complex object types are also serialized to a JSON formatted String and then parsed back into an object at the receiving end.

On the Java side, we use Jackson JSON Processor for conversion back and forth between Java data objects and JSON formatted string representations of those objects. On the JavaScript side, to parse a JSON formatted string, we usejQuery.parseJSON() method. On the other hand, generating the JSON formatted string in JavaScript is done programmatically. As we further review the code below, those functional areas will be described in more detail.

The ContactsActivity class is an android.app.Activity and the entry point to our tutorial application. For more about Android Activity class seehttp://developer.android.com/guide/topics/fundamentals.html. TheContactsActivity class is also the Android back-end interface to be used by JavaScript code in HTML pages representing the UI.

When our tutorial application loads, the ContactsActivity.onCreate() method is called.

Important steps in onCreate() methods are as follows:

  • We initialize a WebView instance and enable it to run JavaScript.
  • Every contact created by our application must be associated with anandroid.accounts.Account. We will utilize an account specific to our application. The type of the application is com.jquerymobile.demo.contact, named after the application package. Using theandroid.accounts.AccountManager class, we search if there is any account by that type. If so, we initialize the accountName field to that name.
  • We initialize the handler field.
  • The ContactsActivity instance is bound to JavaScript as an object under name contactSupport.
  • Finally, we load the index.html file in the WebView. This is the only HTML file with hardcoded name in our Java code. Names of all other HTML files are passed to the methods of ContactsActivity as parameters.

The listing for index.html is given below. All that file does is to call, in jQuery$(document).ready() function, the ContactsActivity.loadPage() method passing'ListPage.html' as a parameter. As a result, the WebView loads ListPage.html.

The ListPage.html has three content pages in it (see Figure 7). Those correspond to the ‘Create Account’ and ‘Contact List’ screens in Figures 1 and 2, respectively, and a Progress screen such as the one in Figure 5. Recall from previous discussion that only one of those pages will be displayed at any given time.

Please note the following.

  • All three content pages have header, footer and content sections defined. All those sections have data-theme="c" which specifies a particular color theme made available by jQuery Mobile. For details, please see the original reference on jQuery Mobile Theme Framework.
  • By default, jQuery Mobile places a back button in the header section. We control navigation in our program and therefore do not need the back button. As a result, we set data-nobackbtn="true". (For further information on jQuery Mobile navigational model see the original reference on that subject.)
  • The header section in ‘Contact List’ has an Add button that invokes a JavaScript method named addContact() which will be reviewed later. Initially, the ‘Contact List’ consists of an empty list, id="contentList", which will be programmatically populated based on data from the Java back-end.
  • The ‘Create Account’ screen has a button to save name of the account as input by the user. Notice data-inline="true" on the definition of that button. This states that the button should wrap around the text ‘Save’. Without it, the button would fill width of the screen by default. For details, see jQuery Mobile documentation on Inline Buttons.

We continue to explore ListPage.html. The listing below describes how to display one of ‘Create Account’, ‘Contact List’ and Progress screens. In jQuery$(document).ready() function, we initialize several variables representing the header, content and footer sections of the content pages for those screens using the id selector $('#...'). Then, define hidePage(), showPage() functions for each of the content pages. For example, showList() displays the ‘Contact List’ screen and hides the ‘Create Account’ and Progress screens.

The highlighted section below gives the remainder of $(document).ready() function in ListPage.html. The Progress screen is displayed and, via the JavaScript binding,ContactsActivity.getAllContacts() method is called. There are two parameters passed to the method: setContactsList is name of the JavaScript callback function to consume the JSON formatted list of contacts. The second parameter,showAccount, is the JavaScript callback function to be called if there is currently no account set up for this application. Both those callback functions are inListPage.html, the current physical file.

The listing below gives ContactsActivity.getAllContacts() method.

We had partially reviewed that code before. To recap:

  • If this application does not currently have an account created for it, the callback JavaScript function for ‘Create Account’ screen is called. This is done vialoadURL(accountCallbackFunction).
  • If the application has an account,ContactUtility.getAllContactDisplaysJSON() is called to get a JSON formatted list of contacts in the device. The resulting JSON string is passed to the JavaScript callback function to display those contacts. This is done vialoadURL(callbackFunction).
  • A sample JSON formatted string for the contact list is given below. Contacts are grouped in alphabetical order by the first initial of their display names. Each group is identified by the key attribute, whose value is the letter representing the group, and consists of an array of contacts within the group, namedvalues. Each array element has contactId and displayName attributes. The elements in the array are further sorted alphabetically based on thedisplayName. (The section named ‘Getting JSON Formatted List Of Contacts’ below gives a review of the code generating that list.)
JSON Formatted List Of Contacts

When the ContactsActivity.getAllContacts() method invokes the JavaScriptshowAccount() function in ListPage.html, the ‘Create Account’ screen in Figure 1 is shown to user.