How does evaluateJavascript work?
There is an example of the evaluateJavascript method being used in this sample:
https://github.com/GoogleChrome/chromium-webview-samples/tree/master/jsinterface-example
Essentially if the javascript you execute in the WebView returns a value it'll be passed in the callback.
The main thing to note is that the String returned in OnReceiveValue is either a JSON Value, JSON Object or JSON Array depending on what you return.
Things to note about this is if you return a single value, you need to use setLenient(true) on a JSON reader for it to work.
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
// In KitKat+ you should use the evaluateJavascript method
mWebView.evaluateJavascript(javascript, new ValueCallback<String>() {
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@Override
public void onReceiveValue(String s) {
JsonReader reader = new JsonReader(new StringReader(s));
// Must set lenient to parse single values
reader.setLenient(true);
try {
if(reader.peek() != JsonToken.NULL) {
if(reader.peek() == JsonToken.STRING) {
String msg = reader.nextString();
if(msg != null) {
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_LONG).show();
}
}
}
} catch (IOException e) {
Log.e("TAG", "MainActivity: IOException", e);
} finally {
try {
reader.close();
} catch (IOException e) {
// NOOP
}
}
}
});
}
The reason you may still want to use a parser for a string response is it is converted to a JSON value which means it will be wrapped in quotes.
For example if you went:
mWebView.evaluateJavascript("(function() { return 'this'; })();", new ValueCallback<String>() {
@Override
public void onReceiveValue(String s) {
Log.d("LogName", s); // Prints: "this"
}
});
It would print the string this, wrapped in double quotes: "this".
Other examples worth noting:
mWebView.evaluateJavascript("(function() { return null; })();", new ValueCallback<String>() {
@Override
public void onReceiveValue(String s) {
Log.d("LogName", s); // Prints the string 'null' NOT Java null
}
});
mWebView.evaluateJavascript("(function() { })();", new ValueCallback<String>() {
@Override
public void onReceiveValue(String s) {
Log.d("LogName", s); //s is Java null
}
});
mWebView.evaluateJavascript("(function() { return ''; })();", new ValueCallback<String>() {
@Override
public void onReceiveValue(String s) {
Log.d("LogName", s); // Prints "" (Two double quotes)
}
});
OK, so it turns out the result
here is the result of the Javascript call - as if one were entering the command into a Javascript console.
So in order to get a result, it needs to be wrapped in a function:
webView1.evaluateJavascript("(function() { return \"this\"; })();", new ValueCallback<String>() {
@Override
public void onReceiveValue(String s) {
Log.d("LogName", s); // Prints 'this'
}
});
This will also work:
webView1.evaluateJavascript("window.variable = \"asd\";", new ValueCallback<String>() {
@Override
public void onReceiveValue(String s) {
Log.d("LogName", s); // Prints asd
}
});
The method also handles Javascript objects:
webView1.evaluateJavascript("(function() { return { var1: \"variable1\", var2: \"variable2\" }; })();", new ValueCallback<String>() {
@Override
public void onReceiveValue(String s) {
Log.d("LogName", s); // Prints: {"var1":"variable1","var2":"variable2"}
}
});
AndroidJSCore is a good alternative for evaluating JavaScript that does not use a WebView.
If you want to stick with WebView and need to evaluate JavaScript on earlier versions of Android (4+), here is a little library:
https://github.com/evgenyneu/js-evaluator-for-android
jsEvaluator.evaluate("put your JavaScript code", new JsCallback() {
@Override
public void onResult(final String result) {
// get result here (optional)
}
});
To summarize the answer of @GauntFace and provide an alternative solution without using JSON parser:
If your JS function returns just a String
and you're wondering about why the string is mangled in Java, it's because it's JSON-escaped.
mWebView.evaluateJavascript("(function() { return 'Earvin \"Magic\" Johnson'; })();", new ValueCallback<String>() {
@Override
public void onReceiveValue(String s) {
Log.d("LogName", s);
// expected: s == Earvin "Magic" Johnson
// actual: s == "Earvin \"Magic\" Johnson"
}
});
(note that onReceiveValue
always provides a String
while JS function may return a null
, a Number literal etc.)
To get the string value the same as in JS, if you're 100% sure you're expecting a proper String
returned, you'd need to JSON-unescape it, for example like that:
String unescaped = s.substring(1, s.length() - 1) // remove wrapping quotes
.replace("\\\\", "\\") // unescape \\ -> \
.replace("\\\"", "\""); // unescape \" -> "
However, note that s
might be a string "null"
if JS returns proper null
, so you obviously need to check that as the very first step.
if ("null".equals(s)) {
...
} else {
// unescape JSON
}