JSInterface Problem msgbox

Discussion in 'Android Questions' started by awama, Aug 26, 2011.

  1. awama

    awama Active Member Licensed User

    Hi Martin,

    in the attached example there isn't possible to display a msgbox more than once. What is the reason of this effekt?

    Walter
     

    Attached Files:

  2. warwound

    warwound Expert Licensed User

    Hi.

    I think this is a thread related problem...

    Look at the official documentation for the native WebView addJavascriptInterface method:

    I tried the code you posted and after clicking the Save webpage menu item everything worked as expected except no msgbox displayed.

    I tried again and the log showed this error:

    Code:
    Now you now want to save PageContent
    main_webview1_savewebpage (B4A line: 
    49)
    Msgbox("2","")

    android.view.ViewRoot$CalledFromWrongThreadException: Only the original 
    thread that created a view hierarchy can touch its views.
       at android.view.ViewRoot.checkThread(ViewRoot.java:
    2802)
       at android.view.ViewRoot.requestLayout(ViewRoot.java:
    594)
       at android.view.View.requestLayout(
    View.java:8125)
       at android.view.View.setFlags(
    View.java:4501)
       at android.view.View.setVisibility(
    View.java:3030)
       at android.app.Dialog.hide(Dialog.java:
    254)
       at anywheresoftware.b4a.debug.Debug.wait(Debug.java:
    178)
       at anywheresoftware.b4a.debug.Debug.reachBP(Debug.java:
    223)
       at anywheresoftware.b4a.debug.Debug.ErrorCaught(Debug.java:
    112)
       at b4a.savehtml.main._webview1_savewebpage(main.java:
    400)
       at java.lang.reflect.Method.invokeNative(Native Method)
       at java.lang.reflect.Method.invoke(Method.java:
    521)
       at anywheresoftware.b4a.BA.raiseEvent2(BA.java:
    105)
       at anywheresoftware.b4a.BA.raiseEvent(BA.java:
    89)
       at uk.co.martinpearman.b4a.jsinterface.JSInterface$1B4AJSInterface.CallSub(JSInterface.java:
    79)
       at android.webkit.BrowserFrame.stringByEvaluatingJavaScriptFromString(Native Method)
       at android.webkit.BrowserFrame.stringByEvaluatingJavaScriptFromString(Native Method)
       at android.webkit.BrowserFrame.loadUrl(BrowserFrame.java:
    245)
       at android.webkit.WebViewCore.loadUrl(WebViewCore.java:
    1562)
       at android.webkit.WebViewCore.access$
    1400(WebViewCore.java:52)
       at android.webkit.WebViewCore$EventHub$
    1.handleMessage(WebViewCore.java:948)
       at android.os.Handler.dispatchMessage(Handler.java:
    99)
       at anywheresoftware.b4a.Msgbox.waitForMessage(
    Msgbox.java:196)
       at anywheresoftware.b4a.Msgbox.msgbox(
    Msgbox.java:127)
       at anywheresoftware.b4a.keywords.Common.Msgbox2(Common.java:
    404)
       at anywheresoftware.b4a.keywords.Common.Msgbox(Common.java:
    371)
       at b4a.savehtml.main._webview1_savewebpage(main.java:
    394)
       at java.lang.reflect.Method.invokeNative(Native Method)
       at java.lang.reflect.Method.invoke(Method.java:
    521)
       at anywheresoftware.b4a.BA.raiseEvent2(BA.java:
    105)
       at anywheresoftware.b4a.BA.raiseEvent(BA.java:
    89)
       at uk.co.martinpearman.b4a.jsinterface.JSInterface$1B4AJSInterface.CallSub(JSInterface.java:
    79)
       at android.webkit.BrowserFrame.stringByEvaluatingJavaScriptFromString(Native Method)
       at android.webkit.BrowserFrame.stringByEvaluatingJavaScriptFromString(Native Method)
       at android.webkit.BrowserFrame.loadUrl(BrowserFrame.java:
    245)
       at android.webkit.WebViewCore.loadUrl(WebViewCore.java:
    1562)
       at android.webkit.WebViewCore.access$
    1400(WebViewCore.java:52)
       at android.webkit.WebViewCore$EventHub$
    1.handleMessage(WebViewCore.java:948)
       at android.os.Handler.dispatchMessage(Handler.java:
    99)
       at android.os.Looper.loop(Looper.java:
    123)
       at android.webkit.WebViewCore$WebCoreThread.run(WebViewCore.java:
    621)
       at java.lang.Thread.run(
    Thread.java:1096)
    android.view.ViewRoot$CalledFromWrongThreadException: Only the original 
    thread that created a view hierarchy can touch its views.
    The JSInterface library Java is not part of the main activity thread so cannot access the UI.

    A Google search shows many similar questions.
    Unfortunately all the solutions are based on using the Android SDK and not B4A.

    I'll try researching more later - but suspect that the Java involved for a solution is beyond my present capabilities!

    Martin.
     
  3. awama

    awama Active Member Licensed User

    Hi Martin,

    thanks for your answer.
    "Save WebPage" does works fine and that is important for me.
    I'll separating the "download and save webpage" -function from the main program.
    I do not know JavaScript and my english is not good, so I do not understand much is written. I am not in hurry, but I hope you find a solution.

    Best regards from Bodensee
    Walter
     
  4. agraham

    agraham Expert Licensed User

    @Martin. You can do a cross-thread call to run a Basic4android Sub on the UI thread, though as it is run asynchronously you can't get a return value from it.

    Code:
    public boolean RunOnGuiThread(String sub, Object[] params)
    {
       
    sub sub.toLowerCase();
       
    if (params == null)
          params = new Object[
    0];
       
    if (!ba.subExists(sub))
       {
          
    return false;
       
    }
       ba.raiseEventFromDifferentThread(this, this, 0, sub, true, params);
       return true;
    }
     
  5. warwound

    warwound Expert Licensed User

    Hi agraham.

    That code works perfectly and the message boxes now display as expected.

    So i'm looking at updating the JSInterface library and have a question...

    Here's part of the current (version 1.3) library code:

    Code:
    private BA activity;
    public String CallSub(String subName, String param1, String param2) {
       subName = subName.toLowerCase();
       Object[] params = { param1, param2 
    };
       return (String) activity.raiseEvent(this, subName, params);
    }
    That's the overloaded CallSub method that accepts two String parameters.

    To test out your posted code i did this:

    Code:
    private BA activity;
    private int taskId = 0;
    private String CallSubOnGUIThread(String subName, Object[] params) {
       subName = subName.toLowerCase();
       
    if (params == null) {
          params = new Object[
    0];
       
    }
       if (activity.subExists(subName)) {
          return (String) activity.raiseEventFromDifferentThread(this, this, taskId++, subName, true, params);
       } else {
          return "debug Sub does NOT exist";
       }
    }

    public String CallSub(String subName, String param1, String param2) {
       subName = subName.toLowerCase();
       Object[] params = { param1, param2 };
       return CallSubOnGUIThread(subName, params);
    }
    That code works fine.

    Version 1.3 of JSInterface has four overloaded CallSub methods that can handle from zero up to three String parameters.
    The methods all use the BA.raiseEvent method to call the B4A Sub.

    Now i can't see it being a good idea to keep the existing CallSub methods which do not work with the UI thread and then adding more methods that are UI compatible.
    It'd be a bit much i think to expect the user to research the subject and decide whether their javascript needs to call a Sub which is UI compatible or not.
    Plus it'd also bloat the library with much repeated code.

    [question]
    Is there any reason why i shouldn't simply update the library so that all existing BA.raiseEvent method calls simply use the BA.raiseEventFromDifferentThread method instead?
    So even if a javascript interface method does NOT need to access the UI, the UI compatible raiseEventFromDifferentThread is used anyway.
    [/question]

    Thanks.

    Martin.
     
  6. agraham

    agraham Expert Licensed User

    No as long as the limitations are acceptable.

    1) You can't accept a return value from raiseEventFromDifferentThread. I don't know why Erel gave it an Object return type, the return value is always a null.

    2) You can't know whether a called event has actually run or not, although they will be run in the sequence in which they were called.

    Personally I'd keep both as you may need to look at a Global flag to see if an asynchronous event has run. Not being able to accept return values seems too limiting to me.
     
Loading...
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice