evaluateJavascript是如何工作的?

77

我正在尝试在Android 4.4中使用新的evaluateJavascript方法,但我始终只得到一个空结果:

webView1.evaluateJavascript("return \"test\";", new ValueCallback<String>() {
    @Override
    public void onReceiveValue(String s) {
        Log.d("LogName", s); // Log is written, but s is always null
    }
});
如何将结果返回给这个方法?
更新:更多信息:
1.我已经设置了INTERNET权限 2.我已经使用了“setJavascriptEnabled(true);” 3.尝试使用撇号字符串:return 'test'; 4.尝试使用JS对象:return { test:'this' } 5.console.log('test');已被成功执行。 6.根据If your app uses WebView的说明,将targetSdkVersion设置为19。
设备:Nexus 7和Nexus 5(原生)。

1
你尝试过在JavaScript中删除return关键字吗?所以:webView1.evaluateJavascript("\"test\"", new ValueCallback<String>() { /*callback code here*/ }); - AndroidKotlinNoob
我已经阅读了WebView文档,并试图理解evaluateJavascript函数的实际用例是什么? - AdamHurwitz
6个回答

86

在这个示例中,使用了evaluateJavascript方法的实例:

https://github.com/GoogleChrome/chromium-webview-samples/tree/master/jsinterface-example

基本上,如果您在WebView中执行的JavaScript返回一个值,它将在回调中传递。

需要注意的主要事情是,在OnReceiveValue中返回的字符串取决于您返回的内容,可能是JSON Value、JSON Object或者JSON Array。

需要注意的另一件事是,如果您只返回一个值,则需要对JSON reader使用setLenient(true)才能使其正常工作。

     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
                    }
                }
            }
        });
    }

你可能仍然希望对字符串响应使用解析器的原因是它会被转换为JSON值,这意味着它将被包含在引号中。

例如,如果你输入:

mWebView.evaluateJavascript("(function() { return 'this'; })();", new ValueCallback<String>() {
    @Override
    public void onReceiveValue(String s) {
        Log.d("LogName", s); // Prints: "this"
    }
});

它将打印包含双引号的字符串"this"。

值得注意的其他示例:

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)
    }
});

这是为了解决编译器警告而引入的JSONReader类的使用,该类在Honeycomb中被引入。 - Matt Gaunt
我们如何在Android KITKAT之前评估Javascript?就像检查API一样- if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) - Kalu Khan Luhar
1
@KaluKhanLuhar 你可以使用 webview.loadurl("javascript:(function () {document.body.background='red';});"); - lucidbrot
1
+1 因为指出 onReceiveValue 返回例如 JSON 值。花了很多时间试图弄清楚为什么对 HTML 元素的 innerHTML 调用返回带引号的内容。 - Anigif
我怎样知道需要添加 function() 函数?如果需要返回一个值,就需要添加 return。在 https://developer.android.com/reference/android/webkit/WebView 中没有相关的说明,谢谢。 - anonymous
显示剩余3条评论

18

好的,事实证明这里的result是JavaScript调用的结果,就像在JavaScript控制台中输入命令一样。

因此为了获得结果,它需要被包装在一个函数中:

webView1.evaluateJavascript("(function() { return \"this\"; })();", new ValueCallback<String>() {
    @Override
    public void onReceiveValue(String s) {
        Log.d("LogName", s); // Prints 'this'
    }
});

这种方法也可以起作用:

webView1.evaluateJavascript("window.variable = \"asd\";", new ValueCallback<String>() {
    @Override
    public void onReceiveValue(String s) {
        Log.d("LogName", s); // Prints asd
    }
});

该方法还可以处理JavaScript对象:

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"}
    }
});

9

AndroidJSCore是一个很好的替代方案,用于评估不使用WebView的JavaScript。

如果您想继续使用WebView并需要在早期版本的Android(4+)上评估JavaScript,则可以使用一个小型库:

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)
  }
});

当我在onPageFinished(WebView view, String url)方法中使用它,与webview.evaluareJavaScript()非常相似,但我没有得到与evaluareJavaScript()方法相同的HTML。 - Akshay

9

大家的答案都很好。我只想再补充一点,不要把evaluateJavascript放在带有@JavascripInterface注解的方法中,就像这样:

    @JavascriptInterface  //this is the right annotation
    public void onData(){ 
            mWebView.evaluateJavascript("javascript:executeNext()",null);
    }  

因为这会阻塞JavaBridge线程。如果您想在其中放置evaluateJavascript,请使用以下方式。

    @JavascriptInterface
    public void onData(){
        mWebView.post(new Runnable() {
            @Override
            public void run() {
                mWebView.evaluateJavascript("javascript:executeNext()",null);
            }
        });

    }

我已经阅读了WebView文档,并试图理解evaluateJavascript函数的实际用例是什么? - AdamHurwitz
例如,@AdamHurwitz可以向Webview注入一些脚本以与网站显示进行交互。 - Morris

8
为了总结@GauntFace的答案并提供一种不使用JSON解析器的替代方案:
如果你的JS函数只返回一个字符串,而且你想知道为什么这个字符串在Java中被损坏了,那是因为它被JSON转义了。
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"
    }
});

请注意,onReceiveValue 始终提供一个 String,而 JS 函数可能返回 null、数字字面量等。

如果您百分之百确定期望返回一个正确的 String,则需要对其进行 JSON 反转义以获得与 JS 中相同的字符串值,例如:

String unescaped = s.substring(1, s.length() - 1)  // remove wrapping quotes
                     .replace("\\\\", "\\")        // unescape \\ -> \
                     .replace("\\\"", "\"");       // unescape \" -> "

然而,请注意如果JS返回适当的nulls可能是一个字符串"null",因此您显然需要将其作为第一步进行检查。

if ("null".equals(s)) {
   ...
} else {
   // unescape JSON
}

2

重要提示:
在调用evaluateJavascript方法之前,您需要为您的WebView启用JavaScript。否则您将无法得到结果。

WebSettings settings = yourWebview.getSettings();
settings.setJavaScriptEnabled(true);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接