Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavascriptInterface in Android's WebView: multiple calls to JS cause deadlock

This is the entire Java code I've used. I will explain in more detail below...

public class Test7 extends Activity {
    //debug
    private final static String TAG = "JSInterface";

    private WebView wv;

    private class JSInterface {
        private WebView wv;

        // Variables to manage interfacing with JS
        private String returnValue;
        private boolean canReadReturnValue;
        private Lock lockOnJS;
        private Condition condVarOnJS;

        public JSInterface (WebView wv) {
            this.wv = wv;       
            this.canReadReturnValue = false;
            this.lockOnJS = new ReentrantLock();
            this.condVarOnJS = lockOnJS.newCondition();
        }

        public void setReturnValue(String ret) {
            lockOnJS.lock();
            returnValue = ret;
            canReadReturnValue = true;
            condVarOnJS.signal();
            lockOnJS.unlock();
            Log.d(TAG, "returnValue = " + returnValue);
        }

        public String getReturnValue() {
            Log.d(TAG, "enter in getReturnValue");
            lockOnJS.lock();
            while (!canReadReturnValue) {
                try {
                    Log.d(TAG, "get wait...");
                    condVarOnJS.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
            lockOnJS.unlock();
            Log.d(TAG, "returnValue: " + returnValue);
            return returnValue;
        }

        public String getNewString() {
            wv.loadUrl("javascript:JSInterface.setReturnValue(createNewString())");         
            return getReturnValue();
        }
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        wv = (WebView) findViewById(R.id.webView1);
        wv.getSettings().setJavaScriptEnabled(true);
        wv.addJavascriptInterface(new JSInterface(wv), "JSInterface");
        wv.loadUrl("file:///android_asset/prova7.html");
    }

    public void button1(View v) {
        wv.loadUrl("javascript:func('1')");
    }
}

And it seems work fine.

You can see that I've got a button (that we can call button1), and clicking on it, it tries to execute a JS method, called func().

public void button1(View v) {
    wv.loadUrl("javascript:func('1')");
}

Inside this JS method, I have to call another Java method. This is the code:

function func(id) {
    document.getElementById(id).innerHTML = JSInterface.getNewString();
}

I need to return the result of JSInterface.getNewString() to the innerHTML variable.

The code of JSInterface.getNewString() is this:

public String getNewString() {
    wv.loadUrl("javascript:JSInterface.setReturnValue(createNewString())");         
    return getReturnValue();
}

You can see that I use the method setReturnValue and getReturnValue to return the value returned by another JS method. This is the code:

function createNewString() {
    return "my New String";
}

The problem is that when I try to set the returnValue, the function createNewString is never executed! If I add a console.log() line, my logCat display nothing!

I cannot understand why this happens.

like image 384
Vito Gentile Avatar asked Nov 05 '22 11:11

Vito Gentile


1 Answers

All the javascript and your JSInterface methods called from javascript are running on the single thread in Android WebView. So while you are waiting in condVarOnJS.await() no javascript can be executed, just because it is executed on the same thread.

Moreover, all the webview instances in your application share the same javascript thread.

like image 68
Dmitry Kochin Avatar answered Nov 14 '22 23:11

Dmitry Kochin