Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android WebView evaluateJavaScript Callback is not being invoked

Tags:

evaluateJavascript(String script, ValueCallback<String> resultCallback) method is added to WebView on Android in SDK 19.

Android documentation quotes:

If non-null, resultCallback will be invoked with any result returned from that execution.

I am using this method as shown below, but somehow my callback is not being invoked. I can see from debugging that the evaluateJavascript() is called, but the call back is not being invoked in Android API 19, 20 & 21. From API 22 (LOLLIPOP_MR1) onwards, everything is working as expected.

Calling webview.loadURL("") before evaluateJavascript() makes it work on all the API levels. I want to understand why and would appreciate if somebody can shed some light / share any links about this. If I can understand why, I want to see if calling loadURL() could be avoided. There is another unrelated problem which makes loadURL() a non-preferable solution.

Code:

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

Please find an example of this problem at https://github.com/bashok001/TestApp

like image 307
Ashok Bommisetti Avatar asked Dec 26 '16 01:12

Ashok Bommisetti


1 Answers

From the docs:

Asynchronously evaluates JavaScript in the context of the currently displayed page. In ContentViewCore EvaluateJavascript checks for renderview. Looks like for that particular versions of WebView integrated within 19,20,21 - the RenderView is just not created until loadUrl get called.

I found following FIX that could be relevant:

It's modify WebContentsAndorid native implementation of EvaluateJavaScript from :

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()) {
         ... 
     }
   }
   ...

to :

 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()) {
         ...
        }
      }
   ....

And in ContentViewCore there was following code which passing false as start_renderer:

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

This mean that on WebView built prior to mentioned fix calls to evaluateJavaScript does not create RenderView and as result WebContext can't handle java script execution. So when you use loadUrl you force creation of render view and all starts working as expected.

like image 82
j2ko Avatar answered Sep 24 '22 14:09

j2ko