简体   繁体   中英

Android / Java Multithreading - Thread doesn't continue until main thread done its work

The title might seem kind of vague, but I can't think of another one, so I hope I can make my problem clear:

I'm using an Android WebView and a JavascriptInterface. Communication from my app (Java) towards the WebView (JavaScript) is done like so:

webview.loadUrl("javascript:function()");

This is an asynchronous call and should be handled afterwards by the JavaBridge-Thread.

The other way (Javascript towards Java) works bei calling my JavascriptInterface mainInterface from my Javascript code. I can call Java methods declared with @JavascriptInterface in my JavascriptInterface. This call is also executed by the JavaBridge-Thread.

Before going into more detail, here are the important codes:

ModelActivity.java

public boolean loadPropertyIntoWebview(final String element, final String property, final String value){
    final int requestId = jsCallIndex.incrementAndGet();
    webview.loadUrl("javascript:setProperty('"+element+"', '"+property.toLowerCase()+"', '"+value+"', "+requestId+")");

    return waitForJsReturnValue(requestId, 1000);
}

private boolean waitForJsReturnValue(int index, int timeout){
    long start = System.currentTimeMillis();

    while(true){
        long elapsed = System.currentTimeMillis() - start;
        if(elapsed > timeout){
            System.out.println("JS RESPONSE TIMEOUT");
            break;
        }

        synchronized (jsReturnValueLock) {
            String value = jsReturnValues.remove(index);

            if(value != null){
                if(value.toLowerCase().equals("true")){
                    return true;
                }else{
                    System.out.println("JS BAD RESPONSE");
                    return false;
                }
            }

            long toWait = timeout - (System.currentTimeMillis() - start);
            if (toWait > 0)
                try {
                    System.out.println("WAIT NOW FOR "+toWait+" ms on index "+index);
                    jsReturnValueLock.wait(toWait);
                } catch (InterruptedException e) {
                    break;
                }
            else{
                System.out.println("JS RESPONSE TIMEOUT");
                break;
            }
        }
    }
    return false;
}

public void processJsReturnValue(int index, String value){
    System.out.println("RECEIVED ANSWER "+value+" for index "+index+" WAIT FOR UNLOCK");
    synchronized (jsReturnValueLock) {
        System.out.println("UNLOCKED FOR INDEX "+index);
        jsReturnValues.put(index, value);
        jsReturnValueLock.notifyAll();
    }
}

JavascriptInterface.java

@JavascriptInterface
public void onClick(String elementId, String property, String value){
    final String[] info = elementId.split("_");

    dbHandler.addOrUpdateOutVariable(Integer.parseInt(info[0]), Integer.parseInt(info[1]), property, value);

    ((ModelActivity)context).runOnUiThread(new Runnable() {

        @Override
        public void run() {
            actionManager.fireClickEvent(Integer.valueOf(info[0]), Integer.valueOf(info[1]));
        }
    });
}

@JavascriptInterface
public void processJsReturnValue(String index, String value){
    try{
        ((ModelActivity)context).processJsReturnValue(Integer.parseInt(index), value);
    }catch(NumberFormatException e){
        e.printStackTrace();            
    }
}

What I basically want to do, is get a return value from my JavaScript function setProperty() (ModelActivity -> loadPropertyIntoWebview).

During initialization the method loadPropertyIntoWebview() is called multiple times from the Main-Thread, and always receives an answer from the JavaBridge-Thread via both processJsReturnValue() functions.

The other case, which won't work, is when the user pushes a button in my webview, and invokes

onClick(String elementId, String property, String value)

in my JavascriptInterface. The function call

actionManager.fireClickEvent(Integer.valueOf(info[0]), Integer.valueOf(info[1]));

goes through several classes and functions which I can't explain in detail, but in last instance it will call

loadPropertyIntoWebview(final String element, final String property, final String value) .

This call also comes from the main-Thread, because I explicitly said so in my onClick function:

((ModelActivity)context).runOnUiThread(new Runnable() {

    @Override
    public void run() {
        actionManager.fireClickEvent(Integer.valueOf(info[0]), Integer.valueOf(info[1]));
    }
});

Debugging revealed the following:

During initilization and after calling

webview.loadUrl("javascript:setProperty('"+element+"', '"+property.toLowerCase()+"', '"+value+"', "+requestId+")");

the JavaBridge-Thread immediatley returns into processJsReturnValue() .

When invoked through the user it won't do that. Then it won't go there until the main thread is done with my timedout while(true) loop.

I hope this whole thing isn't too confusing, and someone understands my problem and even has an idea on what's going wrong here.

Try to separate onClick and processJsReturnValue as two different JavascriptInterface classes, create their instances and register them independently. Not sure but seems like now they block each other.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM