Android的WebView中的JavascriptInterface:对JS的多次调用会导致死锁

时间:2011-11-02 13:46:15

标签: javascript android webview

这是我用过的整个Java代码。我将在下面详细解释......

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

似乎工作正常。

你可以看到我有一个按钮(我们可以调用 button1 ),然后点击它,它会尝试执行一个名为 func()的JS方法

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

在这个JS方法中,我必须调用另一个Java方法。这是代码:

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

我需要将JSInterface.getNewString()的结果返回给innerHTML变量。

JSInterface.getNewString()的代码是这样的:

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

您可以看到我使用方法setReturnValuegetReturnValue来返回另一个JS方法返回的值。这是代码:

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

问题是当我尝试设置returnValue时,永远不会执行createNewString函数!如果我添加一个console.log()行,我的logCat什么都不显示!

我无法理解为什么会这样。

3 个答案:

答案 0 :(得分:2)

从javascript 调用的所有javascript和JSInterface方法都在Android WebView 的单个线程上运行。所以当你在condVarOnJS.await()等待时,没有javascript可以被执行,只是因为它是在同一个线程上执行的。

此外,应用程序中的所有webview实例共享相同的javascript线程。

答案 1 :(得分:0)

在Internet Explorer中,我发现了同样的问题。您可以像这样使用setTimeout:

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

答案 2 :(得分:0)

我在该代码中执行了您想要的功能,因为我调用了createNewString(),

我将显示我使用的代码,

在java中 ,

   public String  videoPlay(){
        System.out.println("videoPlay");
        mWebView.loadUrl("javascript:window.demo.setReturnValue(createNewString())");
        return getReturnValue();}

public void setReturnValue(String test){
        rotValue=test;
        System.out.println(test);

    }
    public String getReturnValue(){
        System.out.println("get"+rotValue);
        return rotValue;
    }
HTML中的

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
   <script>
function inform(){
    alert('et');

   document.getElementById('myText').value=window.demo.videoPlay();
   alert('et');
 }
function createNewString() {
    return "my New String";
}
</script>
</head>
<body onload="init()">
<form>
<input type='text' id='myText' />
<input type="button" name="test" value="Click me" onclick="inform()">
</form>
</body>
</html> 

函数getter和setter调用和值也设置,但我的输出日志为..

11-08 19:18:17.155: INFO/System.out(847): videoPlay
11-08 19:18:17.165: INFO/System.out(847): getnull
11-08 19:18:17.875: INFO/System.out(847): my New String

从JS调用的videoPlay和从java调用的createnewString(),但它在设置之前返回值,因为我没有使用锁的目的是什么,即使我尝试使用锁,就像你为此做的那样它会打印

 11-08 19:18:17.155: INFO/System.out(847): videoPlay
    11-08 19:18:17.165: INFO/System.out(847): getnull

使用锁也函数回调工作方式错误,需要锁定工作。