JSInterface Problem msgbox

awama

Active Member
Licensed User
Longtime 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
 

Attachments

  • WebView_JSInterface_Problem.zip
    5.9 KB · Views: 314

warwound

Expert
Licensed User
Longtime User
Hi.

I think this is a thread related problem...

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

The Java object that is bound runs in another thread and not in the thread that it was constructed in.

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:

B4X:
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.
 
Upvote 0

awama

Active Member
Licensed User
Longtime 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
 
Upvote 0

agraham

Expert
Licensed User
Longtime 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.

B4X:
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;
}
 
Upvote 0

warwound

Expert
Licensed User
Longtime 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:

B4X:
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:

B4X:
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.
 
Upvote 0

agraham

Expert
Licensed User
Longtime User
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?
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.
 
Upvote 0
Top