Android WebView evaluateJavaScript 回调未被调用

5
在SDK 19中,Android的WebView添加了evaluateJavascript(String script, ValueCallback<String> resultCallback)方法。
Android文档引用如下:
如果非空,则会使用任何从该执行返回的结果调用resultCallback。
我按照下面的方法使用这个方法,但是我的回调没有被调用。我可以从调试中看到evaluateJavascript()被调用,但在Android API 19、20和21中,回调没有被调用。从API 22(LOLLIPOP_MR1)开始,一切都按预期工作。
在调用evaluateJavascript()之前调用webview.loadURL("")可以使其在所有API级别上正常工作。我想知道为什么,并且如果有人能够解释/分享任何相关链接,我将不胜感激。如果我能理解原因,我想看看是否可以避免调用loadURL()。还有另一个无关的问题,使得loadURL()成为不可取的解决方案。
代码:
    private void webViewTest() {
        WebView webview = new WebView(this);
        webview.getSettings().setJavaScriptEnabled(true);
        Log.d("TEST", "BEFORE"); // LOGGED
        // webview.loadUrl(""); // Enabling this makes it work on all Android versions
        webview.evaluateJavascript("(function(){return 'test'})()", new ValueCallback<String>() {
            @Override
            public void onReceiveValue(String s) {
                Log.d("TEST", "From JS: " + s); // NEVER LOGGED on API 19-21
            }
        });
        Log.e("TEST", "AFTER"); // LOGGED
    }


    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            webViewTest();
        }
    });

请在https://github.com/bashok001/TestApp找到此问题的示例。

从 https://developer.android.com/reference/android/webkit/WebView.html 看,为什么不能使用 loadData()loadDataWithBaseURL() 代替。 - Morrison Chang
只要我使用load...()方法,就会破坏另一个功能。因此,我想了解为什么调用load...()可以使它在19、20、21中全部工作。 - Ashok Bommisetti
1个回答

4

根据文档

当前显示的页面上异步执行JavaScript。在ContentViewCore中,EvaluateJavascript会检查渲染视图。看起来,在19、20、21版本的WebView中,RenderView直到调用loadUrl才被创建。

我发现下面的修复可能相关:

它修改了WebContentsAndorid中EvaluateJavaScript的本机实现:

void WebContentsAndroid::EvaluateJavaScript(JNIEnv* env,
                                             jobject obj,
                                             jstring script,
                                            jobject callback,
                                            jboolean start_renderer) {

   RenderViewHost* rvh = web_contents_->GetRenderViewHost();
   DCHECK(rvh);

   if (start_renderer && !rvh->IsRenderViewLive()) {

     if (!static_cast<WebContentsImpl*>(web_contents_)->
         CreateRenderViewForInitialEmptyDocument()) {
         ... 
     }
   }
   ...

致:

 void WebContentsAndroid::EvaluateJavaScript(JNIEnv* env,
                                                jobject obj,
                                                jstring script,
                                                jobject callback) {
   RenderViewHost* rvh = web_contents_->GetRenderViewHost();
   DCHECK(rvh);
   if (!rvh->IsRenderViewLive()) {
     if (!static_cast<WebContentsImpl*>(web_contents_)->
         CreateRenderViewForInitialEmptyDocument()) {
         ...
        }
      }
   ....

ContentViewCore中,有以下代码将false作为start_renderer传递:

public void evaluateJavaScript(String script, JavaScriptCallback callback) {
    assert mWebContents != null;
    mWebContents.evaluateJavaScript(script, callback, false);
}

这意味着在修复之前构建的 WebView 上调用 evaluateJavaScript 不会创建 RenderView,因此 WebContext 无法处理 JavaScript 执行。因此,当您使用 loadUrl 时,您强制创建渲染视图,并且所有内容都按预期工作。

这是一个很好的解释。谢谢! - Ashok Bommisetti

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