Android Calling JavaScript functions in WebView

JavascriptAndroidWebviewAndroid Webview

Javascript Problem Overview


I am trying to call some javascript functions sitting in an html page running inside an android webview. Pretty simple what the code tries to do below - from the android app, call a javascript function with a test message, which inturn calls a java function back in the android app that displays test message via toast.

The javascript function looks like:

function testEcho(message){
     window.JSInterface.doEchoTest(message);
}

From the WebView, I have tried calling the javascript the following ways with no luck:

myWebView.loadUrl("javascript:testEcho(Hello World!)");
mWebView.loadUrl("javascript:(function () { " + "testEcho(Hello World!);" + "})()");

I did enable javascript on the WebView

myWebView.getSettings().setJavaScriptEnabled(true);
// register class containing methods to be exposed to JavaScript
myWebView.addJavascriptInterface(myJSInterface, "JSInterface"); 

And heres the Java Class

public class JSInterface{

private WebView mAppView;
public JSInterface  (WebView appView) {
		this.mAppView = appView;
	}

	public void doEchoTest(String echo){
		Toast toast = Toast.makeText(mAppView.getContext(), echo, Toast.LENGTH_SHORT);
		toast.show();
	}
}

I've spent a lot of time googling around to see what I may be doing wrong. All examples I have found use this approach. Does anyone see something wrong here?

Edit: There are several other external javascript files being referenced & used in the html, could they be the issue?

Javascript Solutions


Solution 1 - Javascript

I figured out what the issue was : missing quotes in the testEcho() parameter. This is how I got the call to work:

myWebView.loadUrl("javascript:testEcho('Hello World!')");

Solution 2 - Javascript

From kitkat onwards use evaluateJavascript method instead loadUrl to call the javascript functions like below

	if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
		webView.evaluateJavascript("enable();", null);
	} else {
		webView.loadUrl("javascript:enable();");
	}

Solution 3 - Javascript

public void run(final String scriptSrc) { 
		webView.post(new Runnable() {
			@Override
			public void run() { 
				webView.loadUrl("javascript:" + scriptSrc); 
			}
		}); 
	}

Solution 4 - Javascript

I created a nice wrapper to call JavaScript methods; it also shows JavaScript errors in log:

private void callJavaScript(String methodName, Object...params){
    StringBuilder stringBuilder = new StringBuilder();
    stringBuilder.append("javascript:try{");
    stringBuilder.append(methodName);
    stringBuilder.append("(");
    for (int i = 0; i < params.length; i++) {
        Object param = params[i];
        if(param instanceof String){
            stringBuilder.append("'");
            stringBuilder.append(param.toString().replace("'", "\\'"));
            stringBuilder.append("'");
        }
        if(i < params.length - 1){
            stringBuilder.append(",");
        }
    }
    stringBuilder.append(")}catch(error){Android.onError(error.message);}");
    webView.loadUrl(stringBuilder.toString());
}

You need to add this too:

private class WebViewInterface{

    @JavascriptInterface
    public void onError(String error){
        throw new Error(error);
    }
}

And add this interface to your webview:

webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(new WebViewInterface(), "AndroidErrorReporter");

Solution 5 - Javascript

Yes you have the syntax error. If you want to get your Javascript errors and printing statements in your logcat you must implement the onConsoleMessage(ConsoleMessage cm) method in your WebChromeClient. It gives the complete stack traces like Web console(Inspect element). Here is the method.

public boolean onConsoleMessage(ConsoleMessage cm) 
	{
	    Log.d("Message", cm.message() + " -- From line "
	                         + cm.lineNumber() + " of "
	                         + cm.sourceId() );
	    return true;
	}

After implementation you will get your Javascript errors and print statements (console.log) on your logcat.

Solution 6 - Javascript

Modification of @Ilya_Gazman answer

	private void callJavaScript(WebView view, String methodName, Object...params){
	    StringBuilder stringBuilder = new StringBuilder();
	    stringBuilder.append("javascript:try{");
	    stringBuilder.append(methodName);
	    stringBuilder.append("(");
	    String separator = "";
	    for (Object param : params) {		        
	        stringBuilder.append(separator);
	        separator = ",";
	        if(param instanceof String){
	            stringBuilder.append("'");
	        }
	            stringBuilder.append(param.toString().replace("'", "\\'"));
	        if(param instanceof String){
	            stringBuilder.append("'");
	        }

	    }
	    stringBuilder.append(")}catch(error){console.error(error.message);}");
	    final String call = stringBuilder.toString();
	    Log.i(TAG, "callJavaScript: call="+call);
	    

	    view.loadUrl(call);
	}

will correctly create JS calls e.g.

callJavaScript(mBrowser, "alert", "abc", "def");
//javascript:try{alert('abc','def')}catch(error){console.error(error.message);}
callJavaScript(mBrowser, "alert", 1, true, "abc");
//javascript:try{alert(1,true,'abc')}catch(error){console.error(error.message);}

Note that objects will not be passed correctly - but you can serialize them before passing as an argument.

Also I've changed where the error goes, I've diverted it to the console log which can be listened by:

	webView.setWebChromeClient(new CustomWebChromeClient());

and client

class CustomWebChromeClient extends WebChromeClient {
	private static final String TAG = "CustomWebChromeClient";

	@Override
	public boolean onConsoleMessage(ConsoleMessage cm) {
		Log.d(TAG, String.format("%s @ %d: %s", cm.message(),
				cm.lineNumber(), cm.sourceId()));
		return true;
	}
}

Solution 7 - Javascript

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <WebView
        android:id="@+id/webView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</RelativeLayout>

MainActivity.java


import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.webkit.WebView;
import android.webkit.WebViewClient;

import com.bluapp.androidview.R;

public class WebViewActivity3 extends AppCompatActivity {
    private WebView webView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_web_view3);
        webView = (WebView) findViewById(R.id.webView);
        webView.setWebViewClient(new WebViewClient());
        webView.getSettings().setJavaScriptEnabled(true);
        webView.loadUrl("file:///android_asset/webview1.html");


        webView.setWebViewClient(new WebViewClient(){
            public void onPageFinished(WebView view, String weburl){
                webView.loadUrl("javascript:testEcho('Javascript function in webview')");
            }
        });
    }
}

assets file

<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.0//EN" "http://www.wapforum.org/DTD/xhtml-mobile10.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><title>WebView1</title>
<meta forua="true" http-equiv="Cache-Control" content="max-age=0"/>
</head>

<body style="background-color:#212121">
<script type="text/javascript">
function testEcho(p1){
document.write(p1);
}
</script>
</body>

</html>

Solution 8 - Javascript

Here is an example to load js script from the asset on WebView.
Put script to a file will help reading easier I load the script in onPageFinished because I need to access some DOM element inside the script (to able to access it should be loaded or it will be null). Depend on the purpose of the script, we may load it earlier

assets/myjsfile.js

document.getElementById("abc").innerText = "def"
document.getElementById("abc").onclick = function() {
    document.getElementById("abc").innerText = "abc"
}

WebViewActivity

webView.settings.javaScriptEnabled = true
webView.webViewClient = object : WebViewClient() {
   
    override fun onPageFinished(view: WebView?, url: String?) {
        super.onPageFinished(view, url)

        val script = readTextFromAsset("myjsfile.js")
        view.loadUrl("javascript: $script")
    }
}

fun readTextFromAsset(context: Context, fileName: String): String {
    return context.assets.open(fileName).bufferedReader().use { it.readText() 
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser163757View Question on Stackoverflow
Solution 1 - Javascriptuser163757View Answer on Stackoverflow
Solution 2 - JavascriptPrasadView Answer on Stackoverflow
Solution 3 - Javascriptuser340994View Answer on Stackoverflow
Solution 4 - JavascriptIlya GazmanView Answer on Stackoverflow
Solution 5 - JavascriptDineshView Answer on Stackoverflow
Solution 6 - JavascriptLukasz 'Severiaan' GrelaView Answer on Stackoverflow
Solution 7 - JavascriptdariushView Answer on Stackoverflow
Solution 8 - JavascriptLinhView Answer on Stackoverflow