From Java Script to Java and Back

Your Android device can do a number of cool things such as store local data, draw graphics, play music, make calls, and determine its location. Wouldn't it be nice if you could access that functionality from a web page? With an embedded WebView control, you can.

The key is the addJavascriptInterface() method in the WebView class. You can use it to extend the Document Object Model (DOM) inside the embedded browser and to define a new object that JavaScript code can access. When the JavaScript code invokes methods on that object, it will actually be invoking methods in your Android program.

You can call JavaScript methods from your Android program too. All you have to do is call the loadUrl() method, passing it a URL of the form javascript:code-to-execute. Instead of going to a new page, the browser will execute the given JavaScript expression inside the current page. You can call a method, change JavaScript variables, modify the browser document—anything you need.

LocalBrowser

Web View

^Display JavaScript alert^ Call Android from JavaScript Hello from Android

TextView

I Ca IJ JavaScript from Android

Hello fro^ Rrmmror

Alert from JavaScript

Figure 7.4: Communicating between Android and an embedded WebView

To demonstrate calls between JavaScript in the WebView and Java in the Android program, let us now build a program that is half HTML/ JavaScript and half Android (see Figure 7.4). The top part of the application window is a WebView control, and the bottom part is a TextView and Button from the Android user interface. When you click the buttons and links, it makes calls between the two environments.

Start by creating a "Hello, Android" program using these parameters:

Project name: LocalBrowser Build Target: Android 2.2 Application name: LocalBrowser Package name: org.example.localbrowser Create Activity: LocalBrowser Min SDK Version: 8

The user interface for this program will be split into two parts. The first part is defined in the Android layout file, res/layout/main.xml:

Download LocalBrowser/res/layout/main.xml

<?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"> <WebView android:id= "@+id/web_view" android:layout_width= "fill_parent" android:layout_height="fill_parent" android:layout_weight="1.0" /> <LinearLayout android:orientation= "vertical" android:layout_width= "fill_parent" android:layout_height="fill_parent" android:layout_weight="1.0" android:padding="5sp"> <TextView android:layout_width= "fill_parent" android:layout_height="wrap_content" android:textSize="24sp" android:text="@string/textview" /> <Button android:id="@+id/button"

android:text="@string/call_javascript_from_android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize= "18sp" /> <TextView android:id= "@+id/text_view" android:layout_width= "fill_parent" android:layout_height="wrap_content" android:textSize= "18sp" /> </LinearLayout> </LinearLayout>

The second part is the index.html file that will be loaded into the WebView. This file goes in the assets directory, not the res directory, because it's not a compiled resource. Anything in the assets directory is copied verbatim onto local storage when your program is installed. The directory is intended to be used for local copies of HTML, images, and scripts that the browser can view without being connected to the network.

Download LocalBrowser/assets/index.html Line 1 <html>

- <script language="JavaScript">

function callJS(arg) {

5 document.getElementById('replaceme').innerHTML = arg;

- <a href="#" onclick="window.alert('Alert from JavaScript')">

Display JavaScript alert</a>

- <a href="#" onclick="window.android.callAndroid('Hello from Browser')">

Call Android from JavaScript</a>

Line 4 of index.html defines the callJS() function that our Android program will be calling later. It takes a string argument and inserts it at the replaceme tag, which is at line 19.

In Figure 7.4, on page 141, you see two HTML links that are defined starting at line 12. The first one just calls a standard window.alert() function to open a window displaying a short message. The second link, at line 16, calls the callAndroid() method on the window.an-droid object. If you loaded this page into a normal web browser, win-dow.android would be undefined. But since we're embedding a browser into an Android application, we can define the object ourselves so the page can use it.

Next we turn to the Android code in the LocalBrowser class. Here's the basic outline, including all the imports we'll need later:

Download LocalBrowser/src/org/example/localbrowser/LocalBrowser.java

Line 1 package org.example.localbrowser;

- import android.app.Activity;

- import android.os.Bundle; 5 import android.os.Handler;

- import android.util.Log;

- import android.view.View;

- import android.view.View.OnClickListener;

- import android.webkit.JsResult;

10 import android.webkit.WebChromeClient;

- import android.webkit.WebView;

- import android.widget.Button;

- import android.widget.TextView;

- import android.widget.Toast;

- public class LocalBrowser extends Activity {

private static final String TAG = "LocalBrowser"; private final Handler handler = new Handler();

- private WebView webView;

20 private TextView textView;

- private Button button;

©Override

- public void onCreate(Bundle savedlnstanceState) { 25 super.onCreate(savedlnstanceState);

setContentView(R.layout.main);

- // Find the Android controls on the screen webView = (WebView) findViewById(R.id.web_view);

30 textView = (TextView) findViewById(R.id.text_view);

- button = (Button) findViewByld(R.id.button);

Note the initialization of a Handler object at line 18. JavaScript calls come in on a special thread dedicated to the browser, but Android user interface calls can be made only from the main (GUI) thread. We'll use the Handler class to make the transition.

To call Android Java code from JavaScript, you need to define a plain old Java object with one or more methods, like this:

Download LocalBrowser/src/org/example/localbrowser/LocalBrowser.java

/** Object exposed to JavaScript */ private class AndroidBridge {

public void callAndroid(final String arg) { // must be final handler.post(new Runnable() { public void run() {

Log.d(TAG, "callAndroid(" + arg + ")"); textView.setText(arg);

When JavaScript calls the callAndroid() method, the application creates a new Runnable object and posts it on the running queue of the main thread using Handler.post(). As soon as the main thread gets a chance, it will invoke the run( ) method, which will call setText( ) to change the text on the TextView object. Now it's time to tie everything together in the onCreate( ) method. First we turn on JavaScript (it's off by default) and register our bridge to JavaScript:

Download LocalBrowser/src/org/example/localbrowser/LocalBrowser.java

// Turn on JavaScript in the embedded browser webView.getSettings().setJavaScriptEnabled(true);

// Expose a Java object to JavaScript in the browser webView.addJavascriptInterface(new AndroidBridge(), "android");

Then we create an anonymous WebChromeClient object and register it with the setWebChromeClient() method.

Download LocalBrowser/src/org/example/localbrowser/LocalBrowser.java

// Set up a function to be called when JavaScript tries // to open an alert window webView.setWebChromeClient(new WebChromeClient() { @Override public boolean onJsAlert(final WebView view, final String url, final String message, JsResult result) { Log.d(TAG, "onJsAlertC" + view + ", " + url + ", "

+ message + ", " + result + ")"); Toast.makeText(LocalBrowser.this, message, 3000).show(); result.confirm(); return true; // I handled it

The term chrome here refers to all the trimmings around a browser window. If this were a full-blown browser client, we'd need to handle navigation, bookmarks, menus, and so forth. In this case, all we want to do is change what happens with JavaScript code when the browser tries to open a JavaScript alert (using window.alert()). Inside onJsAlert() we use the Android Toast class to create a message window that will appear for a short amount of time (in this case, 3000 milliseconds, or 3 seconds).

Download from Library of Wow! eBook <www.wowebook.com>

Once we finish configuring the WebView, we can use loadUrl() to load the local web page:

Download LocalBrowser/src/org/example/localbrowser/LocalBrowser.java

// Load the web page from a local asset webView.loadUrl("file:///android_asset/index.html");

URLs of the form "file:///android_asset/filename" (note the three forward slashes) have a special meaning to Android's browser engine. As you might have guessed, they refer to files in the assets directory. In this case, we're loading the index.html file defined earlier.

Here is the res/values/strings.xml file for the LocalBrowser example:

Download LocalBrowser/res/values/strings.xml

<?xml version="1.0" encoding="utf-8"?> <resources>

<string name="app_name">LocalBrowser</string> <string name="textview">TextView</string> <string name="call_javascript_from_android">

Call JavaScript from Android </string> </resources>

The last thing we have to do is wire up the button at the bottom of the screen so it will make a JavaScript call (a call from Java to JavaScript).

Download LocalBrowser/src/org/example/localbrowser/LocalBrowser.java

// This function will be called when the user presses the // button on the Android side button.setOnClickListener(new OnClickListener() { public void onClick(View view) {

Log.d(TAG, "onClick(" + view + ")");

webView.loadUrl("javascript:callJS('Hello from Android')");

To do that, we set a listener for button clicks using setOnClickListener(). When the button is pressed, onClick() is called, which turns around and calls WebView.loadUrl(), passing it a JavaScript expression to evaluate in the browser. The expression is a call to the callJS() function defined in index.html.

Run the program now, and try it. When you click "Display JavaScript alert," an Android message window will appear. When you click "Call Android from JavaScript," the string "Hello from Browser" will be displayed in an Android text control. And finally, when you press the "Call JavaScript from Android" button, the string "Hello from Android" is sent to the browser and inserted in the HTML where it will be displayed at the end of the web page.

Sometimes you don't need to display a web page, but you just need to access some kind of web service or other server-side resource. In the next section, I'll show you how to do this.

0 0

Post a comment