Communication between Application and WebView with JavaScript

When you’re developing an Android application you sometimes need to use a WebView to show html base content to the user. The WebView might contain a help text, some other static text, or in some cases, you may need to show dynamic content. In those cases it is helpful to have a two-way communication between the application code and the WebView content.

This tutorial shows how you can achieve communication between these two components. First we’ll go through how to call Javascript functions from the application code and the second part shows how to call application methods from Javascript.

Calling JavaScript from application

Calling Javascript function from application code is really easy and straightforward thing to do, and the only thing you need is access to the WebView object. To call a JavaScript you have two options, you can either call the function and forget about it or you can call the function and listen to the return value.

JavaScript function call

To call the function without listening to the return value you just need to load an url with the loadUrl method like this:

webview.loadUrl("javascript:myTestFunction();");

You can call multiple functions at the same time and you can also insert multiple lines of JavaScript to the same loadUrl call. Although if you’re going to call alert() or confirm() functions, you need to implement a custom WebChromeClient class that implements the onJsAlert and onJsConfirm methods.

JavaScript function call with return value

In order to get the JavaScript function’s return value, you need to use the evaluateJavascript method:

webView.evaluateJavascript("javascript:myTestFunction();", new ValueCallback<String>() {
    @Override
    public void onReceiveValue(String s) {
            // Do what you want with the return value
        }
    });

EvaluateJavascript call takes the function name as the first parameter and the a ValueCallBack instance as the second. The ValueCallBack’s onReceiveValue method is called with the return value when the call has finished. The caveat with evaluateJavascript is that it’s only availble from API level 19 onwards.

Calling application methods from WebView

There’s two ways to call application methods from Javascript. First I’m going to show a fail-safe method which works every time but is a little bit cumbersome to implement. After that I’ll show another method which is more elegant but which has had some issues on some older Android versions.

The failsafe way

The failsafe way to call an application method is to use custom urls. The idea is to redirect the WebView to a custom url and the application will catch the redirect and act accordingly. Of course the application should not go through with the redirect if necessary.

To achieve this you need to create a custom implementation of WebViewClient. In another words, you need to create a class that extends WebViewClient and you need to set your WebView to use this new class. Below is a simple example of a custom WebViewClient implementation:

private class CustomWebViewClient extends WebViewClient {

    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {

        if(url.startsWith("tanelikorri://")) {
            Toast.makeText(WebViewActivity.this, "Custom protocol call", Toast.LENGTH_LONG).show();
            return true;
        }

        return false;
    }
}

Set the WebViewClient to your WebView like this:

WebView webView = (WebView) findViewById(R.id.webView);
webView.setWebViewClient(new CustomWebViewClient());

The CustomWebViewClient class overrides the shouldOverrideUrlLoading method from WebViewClient. This method is where communication from WebView is interpreted. The method checks if an url starts with a custom protocol (tanelikorri://) and if does, it shows a Toast to the user. The custom protocol is used to distinct special communication to the application from a normal page navigation. To fire the shouldOverrideUrlLoading method, you need to add a link to the html file which is loaded to the WebView. This link has to start with the special protocol, for example like this:

<a href="tanelikorri://toast">Show toast!</a>

The example above is very simple and also failsafe although somewhat cumbersome. You can extend the custom protocol with url variables, for example you could create a link to tanelikorri://toast/Hello_World and show all text that follows after tanelikorri://toast/ in the Toast. Although this would work, it would take a lot more work than the second method explained next.

The elegant way

The more simple way to enable communication with the two parts is to use WebView’s addJavaScriptInterface. With this method you can inject an object to the JavaScript context and grant access to the methods from JavaScript. To add an interface you need a class similar to this:

private class JavaScriptInterface {

    @JavascriptInterface
    public void callFromJS() {
        Toast.makeText(WebViewActivity.this, "JavaScript interface call", Toast.LENGTH_LONG).show();
    }
}

Note that the methods need to be public and annotated with @JavascriptInterface so that they can be called from JavaScript.

You’ll also need to register an instance of this class as an interface to the WebView:

webView.addJavascriptInterface(new JavaScriptInterface(), "interface");

Now you’re all set up to invoke callfromJS() from JavaScript. To call the method you, for example, use a custom button:

<button onclick="interface.callFromJS()">JavaScript interface</button>

Remember that you need to use the same object name in the call that you gave as the second parameter to the addJavaScriptInterface() method. In this case the name was interface.

Conclusion

That’s all there is to it. The latter way of calling a method through an interface class is a little bit easier to achieve but it is known to cause crashes on Android 2.3. So if you need to support very old versions of Android, use the failsafe method. But if you’re targeting API level 14 or newer, or building something more complex, then use the latter method. It’s easier to use and requires less boilerplate code.

Source code

Source code for the whole example project is available here

Further reading

Comments

comments powered by Disqus