Android JavaScript Interface

This tutorial will show you how to access native features from a WebView, and how to pass data back to a WebView from native code. The former part, invoking native code from a WebView, is achieved through adding a JavaScript interface and is documented in the Android API guide here. The example in the guide causes an Android Toast to be triggered from the WebView. All very well, but what about having an app or part of an app that runs in a WebView, gets data from the Android OS and, rather than simply calling a native function like Toast, returns the data to the WebView UI? If you look at something like PhoneGap/Cordova, you can access native features and incorporate them into your web app – it is a two-way process (JS -> Native -> JS).

The Hybrid Approach (Native & Web): Why?

I should start with a disclaimer: I’m not close to being authoritative on the debate between Native, Web or Hybrid app development. That said, there are some things that work really well and are quick to put together using jQuery, HTML5 and CSS3 (such as parsing an XML feed and displaying the results). On the other hand, if you were to make an MP3 player app, a WebView probably wouldn’t be your first choice of technology for the user to interface with the app – you would probably have a more responsive, cohesive and debuggable app using a native UI. But it is entirely possible that you could be making an app that might want to use the WebView for some aspects and a native UI for others. One of the downsides to PhoneGap is that it is all or nothing. You wouldn’t have a native app which occasionally starts up an instance of PhoneGap (*), especially on older devices – this would be very slow. Likewise, you probably wouldn’t make a PhoneGap app that spawns separate Activities in the UI thread – it’s not really designed for that and doing so would kind of defeat the purposes and goals of PhoneGap (most sane people would access native functions via their Plugin architecture). One way to think about what we are doing here is creating a ‘custom plugin’ for a bog standard WebView.

The Hybrid Approach: How?

Well, to start things off, I’ll show you the end product (l-r: Native UI with Button, WebView calling Toast, Webview (alert) displaying data obtained natively):
Native UI ButtonInvoking Native Code (Toast)Native Data in WebView

The first two images are pretty straight-forward, to get the WebView UI to invoke Toast, you would be as well following the API guide, as this is what I did. The more interesting part is getting the info from the system and passing it back to the WebView – here is how I did it.
Let’s start with JS code binded to the second ‘Do it’ button in the WebView. This resides in my index.html file in the android assets folder

			function callNativeForResult() {
				var callbackFnName = 'displayNetworkData';
				Android.callNativeForResult(callbackFnName);
			}

The ‘Android’ object here is a reference to my JavaScript Interface, I could have called it anything. The callNativeForResult() method is a piece of native code:

public class JavaScriptInterface{
		Context mContext;
		
		public JavaScriptInterface(Context c) {
			mContext = c;
		}

		public void callNativeForResult(String callback){
			TelephonyManager mTelephonyManager = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
			String country = mTelephonyManager.getNetworkCountryIso();
			String operator = mTelephonyManager.getNetworkOperatorName();
			if (country.length() < 1){
				country = "unknown";
			}
			if (operator.length() < 1){
				operator = "unknown";
			}
			JSONObject json = new JSONObject();
			try{
				json.put("country", country);
				json.put("operator", operator);
			} catch (JSONException e) {
				showToast("JSON Exception");
			}
			myWebView.loadUrl("javascript:" + callback + "(" + json + ")");
		}
		
	}

Once we have the data we need from the system, we invoke the callback function which is another piece of JS in the index.html file. Here we extract the data for the JSON object ‘data’ and alert the user with some information from Android’s TelephonyManager class:

			function displayNetworkData(data) {
				var operator = data.operator;
				var country = data.country;
				alert('Country: ' + country + '\nOperator: ' + operator);
			}

If you want to download this app, you can do so here. Alternatively, if you want to look at some pros and cons of the Web/Native App approaches, here is a video from Google IO 2011 – it was the discussion of the possibilities of going Hybrid (near the end of the video) that initially made me want to look into the potential applications of the WebView.

* 08/11/2012 – I missed this in the PhoneGap API!

Beginning in Cordova 1.9, with the assistance of the CordovaActivity, you can use Cordova as a component in a larger native Android application.