To run the example code that accompanies this article you should install the Android SDK version 1.5 or later along with Eclipse. To learn more about setting up your environment please visit the Android Developers website. See Resources for links.
It is hard to imagine a technology sector more popular than the mobile phone. Numerous platforms vie for the industry's top marks in terms of sales and mind-share. The devices are advanced engineering specimens, but what really fuels their popularity is the user experience enabled by the vast quantity of applications available for those platforms. In particular, the iPhone and Android platforms are the latest devices dueling for the hearts and wallets of business and consumer clients alike.
The majority of applications available to mobile users are written by mobile developers through the core SDKs provided by the respective platform vendors. The popularity of mobile devices coupled with the large number of talented web technology programmers who have earned their stripes across the Web over the past decade has led to a new model of application—the hybrid application, an application that uses both web browser interfaces and native mobile components. Hybrid applications exist for both iPhone and Android, though the focus in this article is on Android hybrid applications and the use of JavaScript and JSON.
The hybrid application is built around the WebKit engine found in Android's WebView control. This is a user interface widget, which exposes the WebKit's capabilities to an Android programmer. The control can be used to render remote web pages within an application, to provide a familiar user interface experience for developers and users alike, and to leverage the powerful and flexible JavaScript environment within a native Android application.
Hybrid applications often leverage the WebView widget to harness the power of WebKit primarily for user interface elements, however hybrid applications are more than simply displaying some HTML in the widget. Hybrid applications are versatile—the breadth of functionality contained in the Android SDK combined with the web technologies of HTML, CSS and JavaScript, makes hybrid applications limited only by your imagination. To put some flesh on the concept of a hybrid application, this article examines a sample application named AndroidJSON, which implements a number of interactions between an Activity, a WebView and the use of JSON to exchange data. This application demonstrates a number of interactions between the Activity and the HTML and JavaScript hosted by the WebView with the primary feature being a JavaScript calculator.
First, you'll look at embedding a WebKit engine directly into an Android application.
A JavaScript calculator embedded into Android
The majority of SDK-based Android applications contains one or more implementations of
the Activity class. The Activity class is essentially a screen or page that contains the user interface elements experienced by the application user.
The Activity displays a collection of programmer-defined
user interface elements such as buttons, labels, text entry boxes, radio buttons
lists, and so on. All the expected items are present in the Android SDK. In addition
to these user interface staples, there is a special widget, known simply as the
WebView.
The JavaScript calculator demonstrates the symbiotic relationship between the Java environment of the Activity and the JavaScript environment of the WebView. The application reaches beyond simply asking the WebView to display HTML content—it actually wires-up the Java environment to provide functionality to the JavaScript environment and in doing so tightly integrates the two, enabling a unique user experience. Once the two environments are connected, data is exchanged in the form of JSON to deliver various features, all of which is explained thoroughly in this article. Let's begin by looking at how the JavaScript calculator leverages the WebView widget.
Before diving into the specifics of how the application is constructed, take a moment to review the various features of the application. Figure 1 shows the application screen
Figure 1. Demonstrating the JavaScript calculator in action
In the sample native Android application, named AndroidJSON, the screen is defined using the
Activity component. It contains traditional user interface
elements in the top half of the screen such as a TextView (a
static label), an EditText (a text box in which the
user enters a formula), and three buttons (Simple, Complex, and Make Red). Activity also has a single instance of a WebView control, which displays the bottom half of the screen.
The WebView displays an HTML file (index.html) that is packaged with the Android application (though you might download the file from the Internet as well). This web page contains the heading, some sample text, the results of the calculation, and six buttons to perform various functions (Log Info, Log Error, Dynamic, How Many Calls, History, and Kill This App).
The files of interest in this project are AndroidJSON.java (the Android application code>, index.html (the web page), and main.xml (a UI layout file which you will look at later). See Download for links to these files.
First, examine the function of the three buttons in the Activity:
- Simple
- The Simple button causes the contents of the EditText to be evaluated as a mathematical expression. Note that the contents of the EditText, or the formula, are passed to the WebView control and evaluated in JavaScript.
- Complex
- The Complex button sends a JSON object to the WebView for evaluation. This is considered complex due to the fact that the object is subsequently interpreted in JavaScript code and manipulated mathematically. This button alternates between a function to add elements of an array of integers and a function to multiply the same array of integers.
- Make Red
- This third button is largely here for fun. When selected, this button applies a style
to the embedded WebView's content, turning text elements contained within the
<body>tag red.
Now examine the function within the index.html file, enabled at run time by the embedded WebView control.
- Log Info
- This button invokes a callback in the Android application to write an entry to the application log under the Info category.
- Error Info
- This button invokes a callback in the Android application to write an entry to the application log under the Error category.
- Dynamic
- This button invokes a callback in the Android application to retrieve a piece of text
that represents valid JavaScript code. This code is brought back into the WebView and
executed, demonstrating interaction between both sides of the application. Note that
this approach is vulnerable to some security exploits due to a blind reliance upon the
JavaScript
evalfunction. However, your focus here is on the plumbing, not a complete production-ready application. - How many calls
- Each time one of the callback functions is invoked, a counter is incremented. This button simply displays the counter.
- History
- Each time one of the JavaScript functions is invoked, a string representing the function name is added to a JavaScript array. When the history button is invoked, this array is converted to JSON and passed to the native portion of the Android application. The array is reconstituted as an object in the Java code, and enumerated with each element of the array written to the log.
- Kill This App
- This button is another one of the just for fun features of this application. This button invokes a callback which terminates the Android activity through a call to
finish().
Like many applications under development, this Android application makes use of the Logging capabilities built within Android. Some of the screen captures shared in this article come from the Dalvik Debug Monitor Service (DDMS) view within Eclipse, where the LogCat window is visible. For more information on how to use the Android development tools, please refer to the links in Resources.
With this explanation of the function of the application out of the way, now look at how the user interface is constructed.
Creating the user interface for this application involves all three of the previously introduced files. Begin with the layout file, main.xml, in Listing 1.
Listing 1. main.xml, the user interface layout file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView android:layout_width="fill_parent"
android:layout_height="wrap_content" android:text="@string/title" />
<EditText android:id="@+id/formula" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:text="" android:visible="False" />
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<Button android:text="Simple" android:id="@+id/btnSimple"
android:layout_width="wrap_content" android:layout_height="wrap_content">
</Button>
<Button android:text="Complex" android:id="@+id/btnComplex"
android:layout_width="wrap_content" android:layout_height="wrap_content">
</Button>
<Button android:text="Make Red" android:id="@+id/btnRed"
android:layout_width="wrap_content" android:layout_height="wrap_content">
</Button>
</LinearLayout>
<WebView android:layout_width="fill_parent" android:layout_height="fill_parent"
android:id="@+id/calculator" android:layout_weight="1" />
</LinearLayout>
|
In Listing 1, the layout contains the various user interface elements. Note that the presence of the android:id attribute enables the application to make reference to a specific widget within the layout. For example, the WebView contains an id of calculator; however, the TextView does not contain an id as its value is not changed throughout the lifetime of the application.
The onCreate() method found in AndroidJSON.java is responsible for inflating the layout as in Listing 2.
Listing 2. Setting up the user interface
public class AndroidJSON extends Activity {
private final String tag = "AndroidJSON";
private WebView browser = null;
private int flipflop = 0;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final EditText formula = (EditText) this.findViewById(R.id.formula);
final Button btnSimple = (Button) this.findViewById(R.id.btnSimple);
final Button btnComplex = (Button) this.findViewById(R.id.btnComplex);
final Button btnRed = (Button) this.findViewById(R.id.btnRed);
// remaining code removed for brevity - shown in next listings
}
|
You inflate the layout with a call to setContentView(). Note
that you set up the user interface elements by calling the findViewById() method. The R.java file is automatically
generated each time the main.xml file is saved. Layout elements containing the android:id attribute become values within the R.id class, as in Listing 3.
Listing 3. R.java
/* AUTO-GENERATED FILE. DO NOT MODIFY.
*
* This class was automatically generated by the
* aapt tool from the resource data it found. It
* should not be modified by hand.
*/
package com.msi.androidjson;
public final class R {
public static final class attr {
}
public static final class drawable {
public static final int icon=0x7f020000;
}
public static final class id {
public static final int btnComplex=0x7f050002;
public static final int btnRed=0x7f050003;
public static final int btnSimple=0x7f050001;
public static final int calculator=0x7f050004;
public static final int formula=0x7f050000;
}
public static final class layout {
public static final int main=0x7f030000;
}
public static final class string {
public static final int app_name=0x7f040001;
public static final int title=0x7f040000;
}
}
|
You will revisit the Button setup code later in this article; for now focus on
the setup of the WebView control or widget. While the Button and other user interface
elements are rather straightforward, the WebView takes a noticeably greater effort.
Don't worry though—it is not that difficult, particularly considering the tried and true cut and paste technique often applies! Look at the snippet in Listing 4, which is again taken from the onCreate() method found in AndroidJSON.java.
Listing 4. Setting up the WebView widget
// connect to our browser so we can manipulate it
browser = (WebView) findViewById(R.id.calculator);
// set a webview client to override the default functionality
browser.setWebViewClient(new wvClient());
// get settings so we can config our WebView instance
WebSettings settings = browser.getSettings();
// JavaScript? Of course!
settings.setJavaScriptEnabled(true);
// clear cache
browser.clearCache(true);
// this is necessary for "alert()" to work
browser.setWebChromeClient(new WebChromeClient());
// add our custom functionality to the javascript environment
browser.addJavascriptInterface(new CalculatorHandler(), "calc");
// uncomment this if you want to use the webview as an invisible calculator!
//browser.setVisibility(View.INVISIBLE);
// load a page to get things started
browser.loadUrl("file:///android_asset/index.html");
// allows the control to receive focus
// on some versions of Android the webview doesn't handle input focus properly
// this seems to make things work with Android 2.1, but not 2.2
// browser.requestFocusFromTouch();
|
In Listing 4, note that you wired an Activity scoped variable
named browser is to the WebView control. The WebView is a fairly complex class that can be highly customized. For
instance, you need to set up a couple of classes to get the
expected function associated with a web browser. This is one of those exercises where
the programmer must put in a certain minimum amount of effort to get some useful function. However, the sky is the limit in just how far to take this customization. For the purposes of this application, the WebView control is somewhat minimally deployed.
The WebViewClient provides hooks for capturing
various events such as page load start and end, form re-submission, keyboard
intercepting, and many other events programmers love to trap and manipulate.
Similarly, you need an instance of the WebChromeClient for
permitting functions such as the very helpful alert()
JavaScript function. You use the WebSettings class to enable JavaScript for the control.
To cause the WebView control to navigate to a page, you have a couple of different
options. In this application, you employ the loadurl()
method with a fully qualified path for the index.html file packaged as an asset of the project. For more information on setting up the WebView control, examine the documentation for the android.webkit package online (see Resources).
The file named index.html is loaded into the Webview control directly from the resources
shipped with the application. Note in Figure 2 the assets folder under
the resources. This folder is the ideal place to store html files for use in a hybrid application. (View a text-only version of Figure 2.)
Figure 2. The project in Eclipse
Arguably the most important and interesting aspects of working with the WebView is the next step: connecting the WebView's JavaScript environment to the Android Activity code.
Wiring up the JavaScript interface
The next step is to enable the Java code within the Activity to interact with the JavaScript code within the HTML file managed by the WebView. This is accomplished with a call to the addJavascriptInterface() method as in Listing 4.
The arguments to this function are an instance of a Java class and a namespace
identifier. For example, for this application, you define a namespace of calc and implement the code in a class named CalculatorHandler as in Listing 5.
Listing 5.
CalculatorHandler implementation
// Javascript handler
final class CalculatorHandler
{
private int iterations = 0;
// write to LogCat (Info)
public void Info(String str) {
iterations++;
Log.i("Calc",str);
}
// write to LogCat (Error)
public void Error(String str) {
iterations++;
Log.e("Calc",str);
}
// sample to retrieve a custom - written function with the details provided
// by the Android native application code
public String GetSomeFunction()
{
iterations++;
return "var q = 6;function dynamicFunc(v) { return v + q; }";
}
// Kill the app
public void EndApp() {
iterations++;
finish();
}
public void setAnswer(String a)
{
iterations++;
Log.i(tag,"Answer [" + a + "]");
}
public int getIterations()
{
return iterations;
}
public void SendHistory(String s)
{
Log.i("Calc","SendHistory" + s);
try {
JSONArray ja = new JSONArray(s);
for (int i=0;i<ja.length();i++) {
Log.i("Calc","History entry #" + (i+1) + " is [" + ja.getString(i)
+ "]");
}
} catch (Exception ee) {
Log.e("Calc",ee.getMessage());
}
}
}
|
Within the JavaScript environment, you access the CalculatorHandler's methods through the window.calc.methodname syntax. For example, the CalculatorHandler implements a method named Info(), which takes a string argument and writes it to the application log. To access this method from the JavaScript environment, use syntax such as this: window.calc.Info("write this string to the application log!");.
With this basic understanding of how the Java code is invoked from JavaScript code, examine the index.html file in Listing 6 to see how the various methods are invoked.
Listing 6. index.html rendered (and executed) in WebView control
<html>
<head>
<meta name="viewport" content="width=device-width,initial-scale=0.25,
user-scalable=yes" />
<title>Android to JavaScript with JSON</title>
</head>
<script language="JavaScript">
var cmdHistory = new Array();
function startup() {
try {
window.calc.Info("Starting up....");
cmdHistory[cmdHistory.length] = "startup";
} catch (ee) {
}
}
function PerformSimpleCalculation(formula) {
try {
cmdHistory[cmdHistory.length] = "PerformSimpleCalculation";
var answer = eval(String(formula));
document.getElementById('data').value = answer;
window.calc.setAnswer(answer);
} catch (ee) {
window.calc.Error(ee);
}
}
function PerformComplexCalculation(andmethod) {
try {
/*
* argument to this function is a single object with 2 "members or properties"
* operation: this is a string naming what we want the function to do.
* array of arguments: this is an array of integers
*
*/
//alert(andmethod.operation);
//alert(andmethod.arguments.length);
if (andmethod.operation == "addarray") {
cmdHistory[cmdHistory.length] = "PerformCompleCalculation-addarray";
var i;
var result = 0;
for (i=0;i<andmethod.arguments.length;i++) {
result += andmethod.arguments[i];
}
document.getElementById('data').value = result;
window.calc.setAnswer(result);
}
if (andmethod.operation == "multarray") {
cmdHistory[cmdHistory.length] = "PerformCompleCalculation-multarray";
var i;
var result = 1;
for (i=0;i<andmethod.arguments.length;i++) {
result *= andmethod.arguments[i];
}
document.getElementById('data').value = result;
window.calc.setAnswer(result);
}
} catch (ee) {
window.calc.Error(ee);
}
}
function dynamicfunction()
{
try {
cmdHistory[cmdHistory.length] = "PerformCompleCalculation-dynamic";
eval(String(window.calc.GetSomeFunction()));
var result = dynamicFunc(parseInt(document.getElementById('data').value));
document.getElementById('data').value = result;
}catch (ee) {
alert(ee);
}
}
</script>
<body >
<center>
<h3>Running in Web View :)</h3>
this is some sample text here <br />
<input type="text" id="data" value="starting value"><br />
<button onclick="window.calc.Info(document.getElementById('data').value);">Log
Info</button>
<button onclick="window.calc.Error(document.getElementById('data').value);">Log
Error</button><br />
<button onclick="dynamicfunction();">Dynamic</button>
<button onclick="alert(String(window.calc.getIterations()));">How
Many Calls</button>
<button onclick="window.calc.SendHistory(JSON.stringify(cmdHistory));">
History</button>
<button onclick="if (window.confirm('End App?')) window.calc.EndApp();">Kill This
App</button><br />
</center>
</body>
</html>
|
Examine the button handlers towards the end of Listing 6. Essentially these are calling methods in the window.calc namespace, which are implemented in the CalculatorHandler class in AndroidJSON.java.
Listing 5 and Listing 6 work hand in hand to demonstrate the code interaction initiated in the JavaScript environment and implemented in the Java source file. But how about initiating some action from the Activity code that you want to take place within the WebView?
It is time to look deeper into the Java code.
Start with the task of passing a mathematical formula to the JavaScript code for
evaluation. One of the wonderful (and dangerous!) features of JavaScript is the eval() function. The eval() function
permits the run-time evaluation of a string of code. In this example, you take a
string from the EditText control and pass to the JavaScript environment for
evaluation. Specifically, we invoke the PerformSimpleCalculation() function found in Listing 6.
Listing 7 includes the code from AndroidJSON.java which is responsible for handling the button selections.
Listing 7. Calling the
PerformSimpleCalculation() JavaScript function from Java
btnSimple.setOnClickListener(new OnClickListener()
{
public void onClick(View v) {
Log.i(tag,"onClick Simple");
// Perform action on click
try
{
String formulaText = formula.getText().toString();
Log.i(tag,"Formula is [" + formulaText + "]" );
browser.loadUrl("javascript:PerformSimpleCalculation(" + formulaText + ");");
}
catch (Exception e)
{
Log.e(tag,"Error ..." + e.getMessage());
}
}
});
|
Despite the many lines of this method, the only one to focus on here is the browser.loadurl() line which passes a string of the format: javascript:<code to execute>.
This JavaScript code is injected into the WebView's current page and executed. In this way, the Java code can execute JavaScript code defined in the WebView.
In the Simple example, a string is passed. However, what about when you need to work
with a more complex structure? Here is where JSON can be an aid. Listing 8 shows the invocation of the PerformComplexCalculation() function, which is in Listing 6.
Listing 8. Calling a more complex function by passing a JSON object
btnComplex.setOnClickListener(new OnClickListener()
{
public void onClick(View v) {
Log.i(tag,"onClick Complex");
// Perform action on click
try
{
String jsonText = "";
if (flipflop == 0)
{
jsonText = "{ \"operation\" : \"addarray\",\"arguments\" :
[1,2,3,4,5,6,7,8,9,10]}";
flipflop = 1;
} else {
jsonText = "{ \"operation\" : \"multarray\",\"arguments\" :
[1,2,3,4,5,6,7,8,9,10]}";
flipflop = 0;
}
Log.i(tag,"jsonText is [" + jsonText + "]" );
browser.loadUrl("javascript:PerformComplexCalculation(" + jsonText + ");");
}
catch (Exception e)
{
Log.e(tag,"Error ..." + e.getMessage());
}
}
});
|
Examine the JavaScript function PerformComplexCalculation
from Listing 6. Note that the argument passed in is not a string,
but rather an object of your own creation.
operation- Name of a function or procedure to processarguments- This is an array of integers
The object contains only two properties, but can be arbitrarily more complex to meet a greater need.
In this example, the PerformComplexCalculation() JavaScript
function supports two different operations: addarray and multarray. When these have
done their work upon invocation, the result is passed back to the Java code by calling
the function: window.calc.setAnswer. Here you see
bi-directional data flows between the Java and JavaScript code.
In this example, you passed a JSON object, but one observation from experience is that
when working with Java strings coming back from the Java code, it helps to convert
them to JavaScript strings. This can be done by passing the value to the String function as in this example: eval(String(formula));.
The JavaScript eval() function uses JavaScript strings.
Without the conversion, the eval function basically does nothing.
For a somewhat more complex example, you are encouraged to walk through the code sequence when the Dynamic button is selected in the WebView.
To wrap up the code examples, look at passing an array of strings from the JavaScript environment to the Java environment.
The JavaScript code in the example application (index.html) records local function
invocations into a page level array named cmdHistory. Each
time a function is invoked, you add a new entry to this array. For example, when the
dynamicfunction() is invoked, a new string is stored: cmdHistory[cmdHistory.length] = "PerformCompleCalculation-dynamic";.
Nothing is particularly special about this approach; it is simply an example of collecting usage data at the page level. Perhaps this data is useful stored within the database of the Android application. How does this data get back to the Java code?
To send your array of string objects, you call the JSON.stringify function, passing in the array as the argument.
Optionally, the stringify function can allow the customization of how a particular
property of a complex Object is formatted. For more information on how this is
accomplished, you might consider the explanation found at json.org (see Resources).
Figure 3 shows what winds up in the Log after parsing the JSON array following a typical run of the application.
Figure 3. Parsing out the JSON array sent from JavaScript
This example is only storing string data, so arguably you can simply append it to a
longer string and call a simple function in the CalculatorHandler, which in turn can parse it out. However, what if the application wanted to track other data such as the value of certain variables or even attempt to profile the code by recording the duration of a particular function invocation? Clearly the ability to record and exchange objects is of interest in more complex scenarios.
This article demonstrated techniques of transferring data between the Java code in the Android application to JavaScript code in the WebView as well as the more general topic of hybrid applications leveraging WebKit. Hybrid applications mix JavaScript, JSON, callback functions, Android-SDK Java code and the most important ingredient of all, your imagination, to deliver flexible and capable mobile applications.
| Description | Name | Size | Download method |
|---|---|---|---|
| Article source code | andjson.zip | 58KB | HTTP |
Information about download methods
Learn
-
Using XML and JSON with Android, Part 1: Explore the benefits of JSON and XML in Android applications: Consider both XML and JSON data-interchanges formats for use on the Android platform (Frank Ableson, developerWorks, July 2010): Learn techniques for handling XML and JSON on the Android platform in this two-part series. Cover the basics of XML and JSON and build an Android application that parses and displays a Twitter status-update feed provided in both formats in Part 1.
- Develop Android applications with Eclipse (Frank Ableson, developerWorks, February 2008): The easiest way to develop Android applications is to use Eclipse. Learn all about this in this developerWorks tutorial.
- Introduction to Android Development (Frank Ableson, developerWorks, May 2009): Get an introduction to the Android platform and learn how to code a basic Android application.
- Networking with Android (Frank Ableson, developerWorks, June 2009): Explore the networking capabilities of Android.
- Working with XML on Android (Michael Galpin, developerWorks, June 2009): Learn about the different options for working with XML on Android and how to use them to build your own Android applications.
- Introducing JSON: Learn JSON syntax.
- To XML and Back: Using JSON in Android (Frank Ableson, Linux Magazine, March 2010): Read more about JSON and Android.
- Under the Hood of Native Web Apps for Android: Learn about hybrid applications in Android.
- Mobile Design and Development (Brian Fling, O'Reilly Media, 2009): Read about practical guidelines, standards, techniques, and best practices for building mobile products in this book.
- The Open Handset Alliance: Visit Android's sponsor.
- More articles by this author (Frank Ableson, developerWorks, April 2002-current): Read articles about Android, XML, Eclipse, GPS, BlackBerry applications, and other technologies.
- XML area on developerWorks: Get the resources you need to advance your skills in the XML arena.
- My developerWorks: Personalize your developerWorks experience.
- IBM XML certification: Find out how you can become an IBM-Certified Developer in XML and related technologies.
- XML technical library: See the developerWorks XML Zone for a wide range of technical articles and tips, tutorials, standards, and IBM Redbooks. Also, read more XML tips.
- developerWorks technical events and webcasts: Stay current with technology in these sessions.
- developerWorks on Twitter: Join today to follow developerWorks tweets.
- developerWorks
podcasts: Listen to interesting interviews and discussions for software developers.
Get products and technologies
- Android SDK: Download the Android SDK access the API reference, and get the latest news on Android from the official Android developers' site. Version 1.5 and later will work for this article's exercise.
- Eclipse: Obtain the latest Eclipse IDE.
- Android Open Source Project: Android is open source, which means that you can get the source code here.
- JSONLint: Try this online JSON validator.
- IBM product evaluation versions: Download or explore the online trials in the IBM SOA Sandbox and get your hands on application development tools and middleware products from DB2®, Lotus®, Rational®, Tivoli®, and WebSphere®.
Discuss
- XML zone discussion forums: Participate in any of several XML-related discussions.
- developerWorks blogs: Check out these blogs and get involved.
W. Frank Ableson is an entrepreneur living in northern New Jersey with his wife Nikki and their children. His professional interests include mobile software and embedded design. He is the author of Unlocking Android (Manning Publications, 2010), and he is the mobile editor for Linux Magazine.



